<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Florimond Manca</title>
    <description>The latest articles on Forem by Florimond Manca (@florimondmanca).</description>
    <link>https://forem.com/florimondmanca</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%2F89633%2F3cb14893-72e9-40ab-8f02-24c7b7df3f17.JPG</url>
      <title>Forem: Florimond Manca</title>
      <link>https://forem.com/florimondmanca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/florimondmanca"/>
    <language>en</language>
    <item>
      <title>Introduction to ASGI: Emergence of an Async Python Web Ecosystem</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Tue, 31 Dec 2019 13:55:17 +0000</pubDate>
      <link>https://forem.com/florimondmanca/introduction-to-asgi-emergence-of-an-async-python-web-ecosystem-38oi</link>
      <guid>https://forem.com/florimondmanca/introduction-to-asgi-emergence-of-an-async-python-web-ecosystem-38oi</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://florimond.dev/blog/articles/2019/08/introduction-to-asgi-async-python-web/"&gt;florimond.dev/blog&lt;/a&gt; on Nov. 8th, 2019.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;There's a lot of exciting stuff happening in the Python web development ecosystem right now — one of the main drivers of this endeavour is &lt;a href="https://asgi.readthedocs.io/en/latest/"&gt;ASGI&lt;/a&gt;, the Asynchronous Standard Gateway Interface.&lt;/p&gt;

&lt;p&gt;I already mentioned ASGI several times here, in particular when &lt;a href="https://florimond.dev/blog/articles/2018/12/how-i-built-a-web-framework-and-became-an-open-source-maintainer/"&gt;announcing Bocadillo&lt;/a&gt; and &lt;a href="https://florimond.dev/blog/articles/2019/07/introducing-tartiflette-starlette/"&gt;tartiflette-starlette&lt;/a&gt;, but I never really took the time to write a thorough introduction about it. Well, here we are!&lt;/p&gt;

&lt;p&gt;This post is targeted at people interested in recent trends of Python web development. I want to take you on a guided tour about &lt;strong&gt;what ASGI is&lt;/strong&gt; and &lt;strong&gt;what it means for modern Python web development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before we begin, I'd like to announce that I recently created &lt;a href="https://github.com/florimondmanca/awesome-asgi"&gt;awesome-asgi&lt;/a&gt;, an &lt;em&gt;awesome list&lt;/em&gt; to help folks keep track of the ever-expanding ASGI ecosystem. 🙌&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/florimondmanca"&gt;
        florimondmanca
      &lt;/a&gt; / &lt;a href="https://github.com/florimondmanca/awesome-asgi"&gt;
        awesome-asgi
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A curated list of awesome ASGI servers, frameworks, apps, libraries, and other resources
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
awesome-asgi&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://awesome.re" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/3cbfaa1f947ec978205547180a7363399d2aec652c87f91cf4a6f1f332ca610b/68747470733a2f2f617765736f6d652e72652f62616467652d666c61742e737667" alt="Awesome"&gt;&lt;/a&gt;
&lt;a href="https://calver.org" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/e76b4b3053e16046d66c2ba2cfd8d5a380897c42c5c98a8c3ce7aecce63c7fb8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f63616c7665722d59592e4d2e4d4943524f2d3232626664612e737667" alt="Versioning"&gt;&lt;/a&gt;
&lt;a href="https://dev.azure.com/florimondmanca/public/_build/latest?definitionId=15&amp;amp;branchName=master" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/cd73e7c779648acac249bcaf368021726ab7db27c320dff24299fcfd719db93a/68747470733a2f2f6465762e617a7572652e636f6d2f666c6f72696d6f6e646d616e63612f7075626c69632f5f617069732f6275696c642f7374617475732f666c6f72696d6f6e646d616e63612e617765736f6d652d617367693f6272616e63684e616d653d6d6173746572" alt="Build Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A curated list of awesome ASGI servers, frameworks, apps, libraries, and other resources.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This list should help you keep yourself up to date with the most awesome ASGI projects and resources. You can &lt;strong&gt;watch releases&lt;/strong&gt; on this repo to be notified about new entries. If you find anything missing, please &lt;a href="https://github.com/florimondmanca/awesome-asgiCONTRIBUTING.md"&gt;contribute&lt;/a&gt;. ❣️&lt;/p&gt;
&lt;p&gt;&lt;a href="https://asgi.readthedocs.io" rel="nofollow"&gt;ASGI&lt;/a&gt; is a standard interface positioned as a spiritual successor to WSGI. It enables communication and interoperability across the whole Python async web stack: servers, applications, middleware, and individual components.&lt;/p&gt;
&lt;p&gt;Born in 2016 to power the Django Channels project, ASGI and its ecosystem have been expanding ever since, boosted by the arrival of projects such as Starlette and Uvicorn in 2018.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#application-frameworks"&gt;Application frameworks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#authentication"&gt;Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#end-user-applications"&gt;End-user applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#graphql"&gt;GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#monitoring"&gt;Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#proxies"&gt;Proxies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#real-time-web"&gt;Real-time web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/florimondmanca/awesome-asgi#resources"&gt;Resources&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#reference"&gt;Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#publications"&gt;Talks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#toy-projects-and-examples"&gt;Toy projects and examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#tutorials"&gt;Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#security"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#serialization"&gt;Serialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#serverless"&gt;Serverless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#servers"&gt;Servers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/florimondmanca/awesome-asgi#testing"&gt;Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Application frameworks&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Frameworks for building ASGI web applications.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/almarklein/asgineer"&gt;Asgineer&lt;/a&gt; - A…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/florimondmanca/awesome-asgi"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;You can watch releases to be notified of new entries to the list. 👀&lt;/p&gt;

&lt;p&gt;Alright, let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  It all started with async/await
&lt;/h2&gt;

&lt;p&gt;Contrary to JavaScript or Go, Python is not a language that had asynchronous execution baked in from the start. For a long time, executing things concurrently in Python could only be achieved using multithreading or multiprocessing, or resorting to specialized networking libraries such as eventlet, gevent, or Twisted. (Back in 2008, Twisted already had APIs for asynchronous coroutines, e.g. in the form of &lt;a href="http://blog.mekk.waw.pl/archives/14-Twisted-inlineCallbacks-and-deferredGenerator.html"&gt;&lt;code&gt;inlineCallbacks&lt;/code&gt; and &lt;code&gt;deferredGenerator&lt;/code&gt;&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;But this all changed in Python 3.4+. Python 3.4 &lt;a href="https://www.python.org/dev/peps/pep-3156"&gt;added &lt;code&gt;asyncio&lt;/code&gt; to the standard library&lt;/a&gt;, adding support for cooperative multitasking on top of generators and the &lt;code&gt;yield from&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;Later, the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax was &lt;a href="https://www.python.org/dev/peps/pep-0492/"&gt;added in Python 3.5&lt;/a&gt;. Thanks to this, we now had &lt;strong&gt;native coroutines&lt;/strong&gt; independent of the underlying implementation, which opened the gold rush towards Python concurrency.&lt;/p&gt;

&lt;p&gt;And what a rush it was, indeed! Since 3.5 was released, the community has been literally &lt;strong&gt;async-ifying all the things&lt;/strong&gt;. If you're curious, a lot of the resulting projects are now listed in &lt;a href="https://github.com/aio-libs"&gt;aio-libs&lt;/a&gt; and &lt;a href="https://github.com/timofurrer/awesome-asyncio"&gt;awesome-asyncio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, you guessed it — this also means that &lt;strong&gt;Python web servers and apps are moving towards async&lt;/strong&gt;. In fact, all the cool kids are doing it! (&lt;a href="https://github.com/django/deps/blob/master/accepted/0009-async.rst"&gt;Even Django&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  An overview of ASGI
&lt;/h2&gt;

&lt;p&gt;Now, how does ASGI fit in all of this?&lt;/p&gt;

&lt;p&gt;From a 1000-foot perspective, ASGI can be thought of as the glue that allows Python asynchronous servers and applications to communicate with each other. It shares a lot of design ideas with &lt;a href="https://www.python.org/dev/peps/pep-3333"&gt;WSGI&lt;/a&gt;, and is often presented as its spiritual successor with async built-in.&lt;/p&gt;

&lt;p&gt;Here's what this mental model looks like in a diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--47f7_5kF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c45c65uug0ezbqyf5rvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--47f7_5kF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c45c65uug0ezbqyf5rvl.png" alt="" width="641" height="121"&gt;&lt;/a&gt;&lt;/p&gt;
At a very high-level, ASGI is a communication interface between apps and servers.



&lt;p&gt;But in reality, it's a bit more complex than that.&lt;/p&gt;

&lt;p&gt;To find out how ASGI really works, let's take a look at the &lt;a href="https://asgi.readthedocs.io/en/latest/specs/main.html#overview"&gt;ASGI specification&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ASGI consists of two different components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;protocol server&lt;/em&gt;, which terminates sockets and translates them into connections and per-connection event messages.&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;application&lt;/em&gt;, which lives inside a &lt;em&gt;protocol server&lt;/em&gt;, is instanciated once per connection, and handles event messages as they happen.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So according to the spec, what ASGI really specifies is a &lt;a href="https://asgi.readthedocs.io/en/latest/specs/www.html"&gt;message format&lt;/a&gt; and how those messages should be exchanged between the application and the protocol server that runs it.&lt;/p&gt;

&lt;p&gt;We can now revise our diagram into a more detailed version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nz1IuyMq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2mw06bobv5sjjc0r1omb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nz1IuyMq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2mw06bobv5sjjc0r1omb.png" alt="" width="841" height="201"&gt;&lt;/a&gt;&lt;/p&gt;
How ASGI *really* works.



&lt;p&gt;There are many more interesting details to look at, obviously. For example, you can take a look at the &lt;a href="https://asgi.readthedocs.io/en/latest/specs/www.html"&gt;HTTP and WebSocket specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides, although the spec focuses a lot on server-to-application communication, ASGI turns out to encompass &lt;em&gt;much more&lt;/em&gt; than that.&lt;/p&gt;

&lt;p&gt;We'll get to this in a minute, but first…&lt;/p&gt;

&lt;h2&gt;
  
  
  ASGI basics
&lt;/h2&gt;

&lt;p&gt;Now that we've seen how ASGI fits in the Python web ecosystem, let's take a closer look at what it looks like in code.&lt;/p&gt;

&lt;p&gt;ASGI relies on a simple mental model: when the client connects to the server, we instanciate an application. We then feed incoming bytes into the app and send back whatever bytes come out.&lt;/p&gt;

&lt;p&gt;"Feed into the app" here really means &lt;em&gt;call the app&lt;/em&gt; as if it were a function, i.e. something that takes some input, and returns an output.&lt;/p&gt;

&lt;p&gt;And in fact, that's all an ASGI app is — a &lt;em&gt;callable&lt;/em&gt;. The shape of this callable is, again, &lt;a href="https://asgi.readthedocs.io/en/latest/specs/main.html#applications"&gt;defined by the ASGI spec&lt;/a&gt;. Here's what it looks:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The signature of this function is what the "I" in ASGI stands for: an interface which the application must implement for the server to be able to call it.&lt;/p&gt;

&lt;p&gt;Let's take a look at the 3 arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;scope&lt;/code&gt; is a dictionary that contains information about the incoming request. Its contents vary between &lt;a href="https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope"&gt;HTTP&lt;/a&gt; and &lt;a href="https://asgi.readthedocs.io/en/latest/specs/www.html#id1"&gt;WebSocket&lt;/a&gt; connections.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;receive&lt;/code&gt; is an asynchronous function used to receive &lt;em&gt;ASGI event messages&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;send&lt;/code&gt; is an asynchronous function used to send ASGI event messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, these arguments allow you to &lt;code&gt;receive()&lt;/code&gt; and &lt;code&gt;send()&lt;/code&gt; data over a communication channel maintained by the protocol server, as well as know in what context (or &lt;code&gt;scope&lt;/code&gt;) this channel was created.&lt;/p&gt;

&lt;p&gt;I don't know about you, but the overall look and shape of this interface fits my brain really well. Anyway, time for code samples.&lt;/p&gt;
&lt;h2&gt;
  
  
  Show me the code!
&lt;/h2&gt;

&lt;p&gt;To get a more practical feel of what ASGI looks like, I created a minimal Glitch project which showcases a raw ASGI HTTP app served by &lt;a href="https://dev.toa%20popular%20ASGI%20server"&gt;uvicorn&lt;/a&gt;:&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/asgi-hello-world?path=index.html" alt="asgi-hello-world on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;
Feel free to remix and interact with the code above!





&lt;p&gt;Here, we use &lt;code&gt;send()&lt;/code&gt; to send an HTTP response to the client: we send headers first, and then the response body.&lt;/p&gt;

&lt;p&gt;Now, with all these dictionaries and raw binary data, I'll admit that bare-metal ASGI isn't very convenient to work with.&lt;/p&gt;

&lt;p&gt;Luckily, there are higher-level options — and that's when I get to talk about &lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Starlette is truly a fantastic project, and IMO a foundational piece of the ASGI ecosystem.&lt;/p&gt;

&lt;p&gt;In a nutshell, it provides a toolbox of higher-level components such as requests and responses that you can use to abstract away some of the details of ASGI. Here, take a look at this Starlette hello world:&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;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PlainTextResponse&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PlainTextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starlette does have everything you'd expect from an actual web framework — routing, middleware, etc. But I decided to show this stripped-down version to hint you at the real power of ASGI, which is…&lt;/p&gt;

&lt;h2&gt;
  
  
  Turtles all the way down
&lt;/h2&gt;

&lt;p&gt;The interesting and downright &lt;em&gt;game-changing&lt;/em&gt; bit about ASGI is the concept of "&lt;a href="https://simonwillison.net/2019/Jun/23/datasette-asgi/"&gt;Turtles all the way down&lt;/a&gt;", an expression originally coined (I think?) by Andrew Godwin, the person behind Django migrations and now the Django async revamp:&lt;/p&gt;

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

&lt;p&gt;But what does it mean, exactly?&lt;/p&gt;

&lt;p&gt;Well, because ASGI is an abstraction which allows to tell in which context we are and to receive and send data &lt;em&gt;at any time&lt;/em&gt;, there's this idea that ASGI can be used not only between servers and apps, but really &lt;em&gt;at any point in the stack&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, the Starlette &lt;code&gt;Response&lt;/code&gt; object &lt;em&gt;is&lt;/em&gt; an ASGI application itself. In fact, we can strip down the Starlette example app from earlier to &lt;em&gt;just this&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PlainTextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How &lt;em&gt;ridiculous&lt;/em&gt; is that?! 😍&lt;/p&gt;

&lt;p&gt;But wait, there's more.&lt;/p&gt;

&lt;p&gt;The deeper consequence of "Turtles all the way down" is that we can build all sorts of applications, middleware, libraries and other projects, and ensure that they will be &lt;strong&gt;interoperable&lt;/strong&gt; as long as they all implement the ASGI application interface.&lt;/p&gt;

&lt;p&gt;(Besides, from my own experience building &lt;a href="https://bocadilloproject.github.io/"&gt;Bocadillo&lt;/a&gt;, embracing the ASGI interface very often (if not &lt;em&gt;always&lt;/em&gt;) results in much cleaner code.)&lt;/p&gt;

&lt;p&gt;For example, we can build a ASGI middleware (i.e. an app that wraps another app) to display the time a request took to be served:&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;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimingMiddleware&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="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;:.&lt;/span&gt;&lt;span class="mi"&gt;2&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; seconds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use it, we simply wrap it around an 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="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PlainTextResponse&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PlainTextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and it will magically &lt;em&gt;just work&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;uvicorn app:app
INFO: Started server process &lt;span class="o"&gt;[&lt;/span&gt;59405]
INFO: Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO: Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
...
INFO: &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;, 62718&lt;span class="o"&gt;)&lt;/span&gt; - &lt;span class="s2"&gt;"GET / HTTP/1.1"&lt;/span&gt; 200
Took 1.00 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The amazing bit about this is that &lt;code&gt;TimingMiddleware&lt;/code&gt; can wrap &lt;em&gt;any&lt;/em&gt; ASGI app. The inner app here is super simple, but it could be &lt;em&gt;a full-blown, real-life project&lt;/em&gt; (think hundreds of API and WebSocket endpoints) — it doesn't matter, as long as it's ASGI-compatible.&lt;/p&gt;

&lt;p&gt;(There's a more production-ready version of such a timing middleware: &lt;a href="https://github.com/steinnes/timing-asgi"&gt;timing-asgi&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should I care?
&lt;/h2&gt;

&lt;p&gt;While I think interoperability is a strong selling point, there are many more advantages to using ASGI-based components for building Python web apps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed: the async nature of ASGI apps and servers make them &lt;a href="https://www.techempower.com/benchmarks/#section=data-r18&amp;amp;hw=ph&amp;amp;test=fortune&amp;amp;l=zijzen-f&amp;amp;w=zik0zh-zik0zj-e7&amp;amp;d=b"&gt;really fast&lt;/a&gt; (for Python, at least) — we're talking about 60k-70k req/s (consider that Flask and Django only achieve 10-20k in a similar situation).&lt;/li&gt;
&lt;li&gt;Features: ASGI servers and frameworks gives you access to inherently concurrent features (WebSocket, Server-Sent Events, HTTP/2) that are impossible to implement using sync/WSGI.&lt;/li&gt;
&lt;li&gt;Stability: ASGI as a spec has been around for about 3 years now, and version 3.0 is considered very stable. Foundational parts of the ecosystem are stabilizing as a result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In terms of libraries and tooling, I don't think we can say we're there &lt;em&gt;just yet&lt;/em&gt;. But thanks to a very active community, I have strong hopes that the ASGI ecosystem reaches feature parity with the traditional sync/WSGI ecosystem real soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where can I find ASGI-compatible components?
&lt;/h2&gt;

&lt;p&gt;In fact, more and more people are building and improving projects built around ASGI. This includes servers and web frameworks obviously, but also middleware and product-oriented applications such as &lt;a href="https://github.com/simonw/datasette"&gt;Datasette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some of the non-web framework components that I got the most excited about are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/erm/mangum"&gt;Mangum&lt;/a&gt;: ASGI support for AWS Lambda&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/datasette-auth-github"&gt;datasette-auth-github&lt;/a&gt;: GitHub authentication for ASGI apps&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tartiflette/tartiflette-starlette"&gt;tartiflette-starlette&lt;/a&gt; (I wrote this one!): ASGI support for Tartiflette, an async GraphQL engine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While seeing the ecosystem flourish is great, I've personally been having a hard time keeping up with everything.&lt;/p&gt;

&lt;p&gt;That's why, as announced at the beginning of this article I created &lt;a href="https://github.com/florimondmanca/awesome-asgi"&gt;awesome-asgi&lt;/a&gt;. My hope is that it helps everyone keep up with all the awesome things that are happening in the ASGI space. (And seeing that it almost reached 100 stars in a few days, I have a feeling there was indeed a need to colocalize ASGI resources.)&lt;/p&gt;

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

&lt;p&gt;While it might look like an implementation detail, I truly think that ASGI has laid down the foundations for a new era in Python web development.&lt;/p&gt;

&lt;p&gt;If you want to learn more about ASGI, take a look at the various &lt;a href="https://github.com/florimondmanca/awesome-asgi#publications"&gt;publications&lt;/a&gt; (articles and talks) listed in &lt;code&gt;awesome-asgi&lt;/code&gt;. To get your hands dirty, try out any of the following projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.uvicorn.org/"&gt;uvicorn&lt;/a&gt;: ASGI server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;: ASGI framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.encode.io/typesystem/"&gt;TypeSystem&lt;/a&gt;: data validation and form rendering&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.encode.io/databases/"&gt;Databases&lt;/a&gt;: async database library.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/encode/orm"&gt;orm&lt;/a&gt;: asynchronous ORM.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.encode.io/httpx/"&gt;HTTPX&lt;/a&gt;: async HTTP client w/ support for calling ASGI apps (useful as a test client).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These projects were built and are maintained by Encode, which mostly means Tom Christie. There are open discussions on setting up an &lt;a href="https://discuss.encode.io/t/setting-up-a-maintainence-team/721/9"&gt;Encode maintenance team&lt;/a&gt;, so if you were looking for an opportunity to help advance an open source niche, there you go!&lt;/p&gt;

&lt;p&gt;Enjoy your ASGI journey. ❣️&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>HTTPX: Help Build The Future Of Python HTTP</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Wed, 02 Oct 2019 11:47:37 +0000</pubDate>
      <link>https://forem.com/florimondmanca/httpx-for-hacktoberfest-help-build-the-future-of-python-http-16pj</link>
      <guid>https://forem.com/florimondmanca/httpx-for-hacktoberfest-help-build-the-future-of-python-http-16pj</guid>
      <description>&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/encode"&gt;
        encode
      &lt;/a&gt; / &lt;a href="https://github.com/encode/httpx"&gt;
        httpx
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A next generation HTTP client for Python. 🦋
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a href="https://www.python-httpx.org/" rel="nofollow"&gt;&lt;img width="350" height="208" src="https://res.cloudinary.com/practicaldev/image/fetch/s--7_LhKO31--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/encode/httpx/master/docs/img/butterfly.png" alt="HTTPX"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTPX&lt;/strong&gt; &lt;em&gt;- A next-generation HTTP client for Python.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/encode/httpx/actions"&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h9I1UoMM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/encode/httpx/workflows/Test%2520Suite/badge.svg" alt="Test Suite"&gt;
&lt;/a&gt;
&lt;a href="https://pypi.org/project/httpx/" rel="nofollow"&gt;
    &lt;img src="https://camo.githubusercontent.com/0cf0a9d7c345e2f55a00f209fa5f63aeee5c8dc705764d0011b6ec0a8e7b53b0/68747470733a2f2f62616467652e667572792e696f2f70792f68747470782e737667" alt="Package version"&gt;
&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;HTTPX is a fully featured HTTP client library for Python 3. It includes &lt;strong&gt;an integrated
command line client&lt;/strong&gt;, has support for both &lt;strong&gt;HTTP/1.1 and HTTP/2&lt;/strong&gt;, and provides both &lt;strong&gt;sync
and async APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Install HTTPX using pip:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ pip install httpx&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now, let's get started:&lt;/p&gt;
&lt;div class="highlight highlight-text-python-console position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; httpx
&amp;gt;&amp;gt;&amp;gt; r &lt;span class="pl-k"&gt;=&lt;/span&gt; httpx.get(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;https://www.example.org/&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;)
&amp;gt;&amp;gt;&amp;gt; r
&amp;lt;Response [200 OK]&amp;amp;gt
&amp;gt;&amp;gt;&amp;gt; r.status_code
200
&amp;gt;&amp;gt;&amp;gt; r.headers[&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;content-type&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;]
'text/html; charset=UTF-8'
&amp;gt;&amp;gt;&amp;gt; r.text
'&amp;lt;!doctype html&amp;gt;\n&amp;lt;html&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;lt;title&amp;gt;Example Domain&amp;lt;/title&amp;gt;...'&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Or, using the command-line client.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ pip install &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;httpx[cli]&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; The command line client is an optional dependency.&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Which now allows us to use HTTPX directly from the command-line...&lt;/p&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/encode/httpxdocs/img/httpx-help.png"&gt;&lt;img width="700" src="https://res.cloudinary.com/practicaldev/image/fetch/s--mJXAYYd_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/encode/httpxdocs/img/httpx-help.png" alt="httpx --help"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Sending a request...&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/encode/httpxdocs/img/httpx-request.png"&gt;&lt;img width="700" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3c5xwh-5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/encode/httpxdocs/img/httpx-request.png" alt="httpx http://httpbin.org/json"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
Features&lt;/h2&gt;
&lt;p&gt;HTTPX builds on the well-established usability of &lt;code&gt;requests&lt;/code&gt;, and gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A broadly &lt;a href="https://www.python-httpx.org/compatibility/" rel="nofollow"&gt;requests-compatible API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An integrated command-line client.&lt;/li&gt;
&lt;li&gt;HTTP/1.1 &lt;a href="https://www.python-httpx.org/http2/" rel="nofollow"&gt;and HTTP/2 support&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Standard synchronous…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;HTTPX 🦋 is a next generation HTTP client for Python. It is &lt;a href="https://requests.kennethreitz.org"&gt;Requests&lt;/a&gt;-compatible, supports HTTP/2 and HTTP/1.1, has async support, and an ever-expanding community of 35+ awesome contributors. ❤&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTTPX is part of a growing ecosystem of async-capable Python projects lead by the &lt;a href="https://github.com/encode/"&gt;Encode&lt;/a&gt; organization (which backs the &lt;a href="https://www.django-rest-framework.org/"&gt;Django REST Framework&lt;/a&gt;). Some of the most notable projects include &lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt;, &lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;, and &lt;a href="https://www.encode.io/databases/"&gt;Databases&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest&lt;/a&gt; started yesterday, and on behalf of the HTTPX maintenance team I can say we're very excited to be part of it this year!&lt;/p&gt;

&lt;p&gt;Before getting to the contributing guide, a short preview of what HTTPX can do…&lt;/p&gt;

&lt;p&gt;Your first request to dev.to using HTTPX:&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="nn"&gt;httpx&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://dev.to'&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="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'content-type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;'text/html; charset=utf-8'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&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="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;'&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;html lang="en"&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  &amp;lt;head&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;    &amp;lt;met'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_version&lt;/span&gt;
&lt;span class="s"&gt;'HTTP/1.1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Does DEV support HTTP/2?&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="nn"&gt;httpx&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# The top-level API only supports HTTP/1.1 for efficiency reasons,
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# but 'Client' uses HTTP/2 by default.
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://dev.to'&lt;/span&gt;&lt;span class="p"&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="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_version&lt;/span&gt;
&lt;span class="s"&gt;'HTTP/2'&lt;/span&gt;  &lt;span class="c1"&gt;# YAY! 🎉
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;HTTPX can even call into Python web apps (WSGI + ASGI). A Flask example…&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;httpx&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, world"&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="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'http://testserver'&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&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="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="s"&gt;'Hello, world'&lt;/span&gt;  &lt;span class="c1"&gt;# Tada! 🦋
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And it supports &lt;code&gt;async/await&lt;/code&gt;! Let's fire an IPython REPL to try this 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="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;httpx&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="c1"&gt;# We're going to fetch tag pages concurrently...
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&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="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'https://dev.to/t/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&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;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;     &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;         &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hacktoberfest'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;         &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'python'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;         &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'opensource'&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;...:&lt;/span&gt;     &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&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="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;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="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;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="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&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="n"&gt;url&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;responses&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://dev.to/t/hacktoberfest'&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="s"&gt;'https://dev.to/t/python'&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="s"&gt;'https://dev.to/t/opensource'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;(This example uses &lt;code&gt;asyncio&lt;/code&gt;,  but &lt;a href="https://www.encode.io/httpx/async/#trio"&gt;&lt;code&gt;trio&lt;/code&gt; is also supported&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;HTTPX already has a lot of features built-in, but it's still in an alpha stage as some of the areas still need more work.&lt;/p&gt;

&lt;p&gt;So enough of an intro, here's how YOU can help us build the future of Python HTTP.&lt;/p&gt;
&lt;h2&gt;
  
  
  How you can help
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Try it out!
&lt;/h3&gt;

&lt;p&gt;The most straight-forward way to help move HTTPX forward is by &lt;strong&gt;trying it out&lt;/strong&gt; for yourself.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the sample code in the introduction to this post to see HTTPX in action!&lt;/li&gt;
&lt;li&gt;Build a simple application that calls a &lt;a href="https://github.com/public-apis/public-apis"&gt;public API&lt;/a&gt; using HTTPX.&lt;/li&gt;
&lt;li&gt;Migrate an application that uses Requests to make HTTP calls over to HTTPX.&lt;/li&gt;
&lt;li&gt;Use HTTPX as a test client for your web project powered by Flask, Starlette, FastAPI, etc. More on this in &lt;a href="https://www.encode.io/httpx/advanced/#calling-into-python-web-apps"&gt;Calling into Python web apps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Build a web spider that fetches web pages in parallel using HTTPX.&lt;/li&gt;
&lt;li&gt;... And any other idea you feel like trying!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However you decide to try HTTPX out, we'd love to hear about it! Send us a link to a repo, blog post, or anything else you'd like to share via the comments section. :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Contribute!
&lt;/h3&gt;

&lt;p&gt;As of writing this post, there's a whopping list of &lt;a href="https://github.com/encode/httpx/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest"&gt;10 &lt;code&gt;hacktoberfest&lt;/code&gt;-labelled issues&lt;/a&gt;, all up for grabs! Expect more to come!&lt;/p&gt;

&lt;p&gt;Below are some highlights of what's available at the moment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Documentation
&lt;/h4&gt;

&lt;p&gt;Documentation is critical to any software project. It helps new users get started quickly, and existing users find the information they need on how to use a particular feature or piece of API.&lt;/p&gt;

&lt;p&gt;We know that the &lt;a href="https://encode.io/httpx"&gt;HTTPX documentation&lt;/a&gt; needs refining in several places. Feel free to walk through it and submit PRs for anything you think can be improved — be it typos, copy, or missing information. :-)&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/409"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        .post(), .put(), and .patch() functions in API docs missing 'files' parameter
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#409&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/sethmlarson"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--D1drW7RX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/18519037%3Fv%3D4" alt="sethmlarson avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/sethmlarson"&gt;sethmlarson&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/409"&gt;&lt;time&gt;Sep 28, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Our &lt;a href="https://github.com/encode/httpx/blob/master/docs/api.md"&gt;API docs&lt;/a&gt; are missing the &lt;code&gt;files&lt;/code&gt; parameter on &lt;code&gt;.post()&lt;/code&gt;, &lt;code&gt;.patch()&lt;/code&gt;, and &lt;code&gt;.put()&lt;/code&gt; for Client and AsyncClient&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/409"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/404"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Environment Variables docs should justify each usage case.
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#404&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/tomchristie"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5b2yt_5W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars2.githubusercontent.com/u/647359%3Fv%3D4" alt="tomchristie avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/tomchristie"&gt;tomchristie&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/404"&gt;&lt;time&gt;Sep 27, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Our &lt;a href="https://www.encode.io/httpx/environment_variables/" rel="nofollow"&gt;environment variable documentation&lt;/a&gt; should include some justification or context for each case, by doing one of the following for each envvar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make it clear that an environment variable is specific to HTTPX. (Probably only relevant to anything prefixed by &lt;code&gt;HTTPX_&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Make it clear that an environment variable is used as a convention by various tooling, with at least one relevant hyperlink.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eg.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SSLKEYLOGFILE&lt;/code&gt; could give some more context about usage - "The keylog file is designed for debugging purposes only." doesn't make it clear how or when to use it, or which debug tooling it's usful with.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SSL_CERT_FILE&lt;/code&gt; and &lt;code&gt;SSL_CERT_DIR&lt;/code&gt; can include a link to some docs about their convention. Perhaps give an example of other tooling that supports them?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HTTP_PROXY&lt;/code&gt;, &lt;code&gt;HTTPS_PROXY&lt;/code&gt;, &lt;code&gt;ALL_PROXY&lt;/code&gt; can include a link to some docs about their convention. Perhaps give an example of other tooling that supports them?&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/404"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/403"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        More explicit documentation that `httpx.Client` is equivelent to `requests.Session`.
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#403&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/innawe"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--7D_aCK50--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars2.githubusercontent.com/u/20047821%3Fv%3D4" alt="innawe avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/innawe"&gt;innawe&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/403"&gt;&lt;time&gt;Sep 27, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This isn't an issue (sorry if i posted in a wrong place), but i read the httpx doc and didn't find yet a solution.
In requests i use s = requests.session(), how can i do the same thing with httpx?
it will be possible to do something like s = httpx.session()?
Thanks and sorry again.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/403"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Bug fixes
&lt;/h3&gt;

&lt;p&gt;Fact: all software has bugs 🐛, and surely HTTPX is no different!&lt;/p&gt;

&lt;p&gt;Some of the bugs have already been tracked…&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/391"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Host header contains userinfo component
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#391&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/sethmlarson"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--D1drW7RX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/18519037%3Fv%3D4" alt="sethmlarson avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/sethmlarson"&gt;sethmlarson&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/391"&gt;&lt;time&gt;Sep 26, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;code&gt;BaseRequest.prepare()&lt;/code&gt; incorrectly sets the default &lt;code&gt;Host&lt;/code&gt; header to &lt;code&gt;userinfo@example.com:123&lt;/code&gt; if given the following URL: &lt;code&gt;http://userinfo@example.com:123&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We should be removing the userinfo component from the authority before assigning to &lt;code&gt;Host&lt;/code&gt;.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/391"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;…But most of them still need to be discovered, tracked down, and fixed!&lt;/p&gt;

&lt;p&gt;This is also why it's super important that you &lt;strong&gt;give HTTPX a try&lt;/strong&gt;. Your use case may not be one we have thought about yet, or it might be in conditions that haven't be accounted for yet.&lt;/p&gt;

&lt;p&gt;If you find about something that doesn't look right, please &lt;a href="https://github.com/encode/httpx/issues/new"&gt;open an issue&lt;/a&gt; and we'll look into it together!&lt;/p&gt;

&lt;h3&gt;
  
  
  New features
&lt;/h3&gt;

&lt;p&gt;HTTPX already has a lot of features built-in, but we've got ideas on how to push things even further.&lt;/p&gt;

&lt;p&gt;Below are some features you can tackle. If you try out HTTPX and feel like something's missing, please &lt;a href="https://github.com/encode/httpx/issues/new"&gt;open an issue&lt;/a&gt;! We'll discuss it together and will gladly help you get started if you'd like to implement the feature.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/360"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Add support for the NO_PROXY environment variable
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#360&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/sethmlarson"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--D1drW7RX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/18519037%3Fv%3D4" alt="sethmlarson avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/sethmlarson"&gt;sethmlarson&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/360"&gt;&lt;time&gt;Sep 19, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;The &lt;code&gt;NO_PROXY&lt;/code&gt; environment variable is described here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/wget/manual/html_node/Proxies.html" rel="nofollow"&gt;http://www.gnu.org/software/wget/manual/html_node/Proxies.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/psf/requests/blob/840540b6b1f07ef87faab73392c03fbef0dcc9fe/requests/utils.py#L395"&gt;https://github.com/psf/requests/blob/840540b6b1f07ef87faab73392c03fbef0dcc9fe/requests/utils.py#L395&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to accomplish this task we'd need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implementing NO_PROXY, a list of hosts that will bypass the proxy if found within the list.&lt;/li&gt;
&lt;li&gt;Only enabled if &lt;code&gt;trust_env=True&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Documentation updates for the new environment variable.&lt;/li&gt;
&lt;li&gt;Test cases for all code branches.&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/360"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/encode/httpx/issues/349"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        [Feature Request]: raise httpx exception instead of buildin ConnectionResetError and ConnectionError
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#349&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Trim21"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--LYzdv5OW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars1.githubusercontent.com/u/13553903%3Fv%3D4" alt="Trim21 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Trim21"&gt;Trim21&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/encode/httpx/issues/349"&gt;&lt;time&gt;Sep 16, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This is a feature requests instead of bug report.&lt;/p&gt;
&lt;p&gt;I'm using &lt;code&gt;requests&lt;/code&gt; in my web server to request upstream api. So I can handler all &lt;code&gt;requests.RequestException&lt;/code&gt; in my middleware and return a &lt;code&gt;502 upstream&lt;/code&gt; error.&lt;/p&gt;
&lt;p&gt;But after I migrated to &lt;code&gt;httpx&lt;/code&gt;, httpx doesn't have such exception when issuing a http(s) request, it will raise &lt;code&gt;httpx.ConnectTimeout&lt;/code&gt;, buildin &lt;code&gt;ConnectionError&lt;/code&gt; and &lt;code&gt;ConnectResetError&lt;/code&gt;. And last 2 exception are not only raised by &lt;code&gt;httpx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Maybe &lt;code&gt;httpx&lt;/code&gt; should also wrap &lt;code&gt;ConnectionError&lt;/code&gt; and other buildin connection exception?&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/encode/httpx/issues/349"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;There are many ways to contribute to HTTPX, and all contributions are welcome! Once you've selected an issue, documentation improvement, feature request or bug report you'd like to work on, read the &lt;a href="https://www.encode.io/httpx/contributing/"&gt;contributing guide&lt;/a&gt; to get started, and hack away!&lt;/p&gt;

&lt;p&gt;Thank you so much for reading through this post, and we look forward to seeing the amazing contributions you'll make. 💖🦋&lt;/p&gt;

&lt;p&gt;— The HTTPX maintenance team&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How To Upgrade Your VuePress Site To v1.0</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Sat, 15 Jun 2019 00:50:39 +0000</pubDate>
      <link>https://forem.com/florimondmanca/how-to-upgrade-your-vuepress-site-to-v1-0-4lgi</link>
      <guid>https://forem.com/florimondmanca/how-to-upgrade-your-vuepress-site-to-v1-0-4lgi</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.florimond.dev/vuepress-upgrade-1-0"&gt;blog.florimond.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclaimer: I am not a VuePress maintainer, and this is not an official guide.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vuepress.vuejs.org/"&gt;VuePress&lt;/a&gt; is a Vue-powered static site generator. It's great for writing technical documentation, and I've been using it in production for the &lt;a href="https://bocadilloproject.github.io"&gt;Bocadillo docs site&lt;/a&gt; since December 2018.&lt;/p&gt;

&lt;p&gt;I recently learnt that &lt;a href="https://medium.com/@_ulivz/intro-to-vuepress-1-x-7e2b7885f95f"&gt;VuePress v1.0 was now out of beta&lt;/a&gt;! There are many new exciting features. The one I'm most excited about is the new &lt;strong&gt;plugin architecture&lt;/strong&gt;. There's also a brand new &lt;a href="https://github.com/ulivz/vuepress-theme-blog"&gt;blog theme&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Anyway, this means it's time to upgrade! But… how? Well, you're in luck! Today we will explore together how to upgrade your VuePress site from 0.x to 1.x. 🚀&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is based on my experience upgrading VuePress from 0.14 to 1.0.1 for the Bocadillo documentation.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Read the official announcement
&lt;/h2&gt;

&lt;p&gt;Ulivz (the current maintainer of VuePress) has published a thorough blog post: &lt;a href="https://medium.com/@_ulivz/intro-to-vuepress-1-x-7e2b7885f95f"&gt;Intro to VuePress 1.x&lt;/a&gt;. It contains a lot of interesting information on what's new in 1.x, so I highly recommend you skim through it first. Hopefully it will give you even more reasons to upgrade!&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@_ulivz/intro-to-vuepress-1-x-7e2b7885f95f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5eAnDOfQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/96/96/0%2A4qF2FBsZ4IvQ5N3L.jpg" alt="ULIVZ"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@_ulivz/intro-to-vuepress-1-x-7e2b7885f95f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Intro to VuePress 1.x. TL;DR: This post is a speech record of… | by ULIVZ | Medium&lt;/h2&gt;
      &lt;h3&gt;ULIVZ ・ &lt;time&gt;Jun 13, 2019&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hnDHPsJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium-f709f79cf29704f9f4c2a83f950b2964e95007a3e311b77f686915c71574fef2.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  2. Read the migration guide
&lt;/h2&gt;

&lt;p&gt;The VuePress team also put up a &lt;a href="https://v1.vuepress.vuejs.org/miscellaneous/migration-guide.html"&gt;migration guide&lt;/a&gt; on the official documentation site. I also recommend you skim through it, although we'll be covering the main changes later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Prepare your repo
&lt;/h2&gt;

&lt;p&gt;Assuming your project is checked in Git, it's a good idea to create a new branch to explore the upgrading of VuePress. You never know whether things could go wrong, but more importantly you'll be able to review the changes by opening a PR.&lt;/p&gt;

&lt;p&gt;So, go ahead and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; upgrade/vuepress-1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Upgrade VuePress
&lt;/h2&gt;

&lt;p&gt;Enough already, it's time to upgrade! You can install VuePress 1.x using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vuepress@^1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should update &lt;code&gt;package.json&lt;/code&gt; with &lt;code&gt;"vuepress": "^1.0.1"&lt;/code&gt; or similar, and update &lt;code&gt;package-lock.json&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Use the new plugins
&lt;/h2&gt;

&lt;p&gt;One of the main new features of VuePress 1.x is the &lt;a href="https://v1.vuepress.vuejs.org/plugin/"&gt;plugin architecture&lt;/a&gt;. A lot of built-in features were refactored as plugins, so we'll need to update the VuePress configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google Analytics
&lt;/h3&gt;

&lt;p&gt;Google Analytics could previously be configured via a &lt;code&gt;ga&lt;/code&gt; option in the configuration object (see &lt;a href="https://vuepress.vuejs.org/config/#ga"&gt;0.x docs&lt;/a&gt;). It is now handled by the &lt;a href="https://v1.vuepress.vuejs.org/plugin/official/plugin-google-analytics.html"&gt;google-analytics&lt;/a&gt; plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the plugin:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @vuepress/plugin-google-analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;.vuepress/config.js&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;&lt;span class="gd"&gt;- ga: 'UA-12345678-9'
&lt;/span&gt;&lt;span class="gi"&gt;+ plugins: [
+   [
+     '@vuepress/google-analytics',
+     { ga: 'UA-12345678-9' }
+   ]
+ ]
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Instructions adapted from the &lt;a href="https://v1.vuepress.vuejs.org/miscellaneous/migration-guide.html#ga"&gt;migration guide&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;markdown.config&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you were using the &lt;a href="https://vuepress.vuejs.org/config/#markdown-config"&gt;&lt;code&gt;markdown.config&lt;/code&gt; config option&lt;/a&gt; to customize the &lt;code&gt;markdown-it&lt;/code&gt; instance, you should now use the &lt;code&gt;extendMarkdown&lt;/code&gt; option:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;.vuepress/config.js&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;&lt;span class="gd"&gt;- markdown: {
-   config(md) {
-     // ...
-   }
- }
&lt;/span&gt;&lt;span class="gi"&gt;+ extendMarkdown(md) {
+   // ...
+ }
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Instructions adapted from the &lt;a href="https://v1.vuepress.vuejs.org/miscellaneous/migration-guide.html#markdown-config"&gt;migration guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;All other &lt;a href="https://v1.vuepress.vuejs.org/config/#markdown"&gt;Markdown options&lt;/a&gt; are still valid, though.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Worker
&lt;/h3&gt;

&lt;p&gt;One killer feature of VuePress is the built-in Service Worker support. It allows users to access the website offline after they visited it for the first time.&lt;/p&gt;

&lt;p&gt;Previously this was enabled via the &lt;a href="https://vuepress.vuejs.org/config/#serviceworker"&gt;&lt;code&gt;serviceWorker&lt;/code&gt; option&lt;/a&gt;, but there is now a dedicated &lt;a href="https://v1.vuepress.vuejs.org/plugin/official/plugin-pwa.html"&gt;pwa&lt;/a&gt; plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the plugin:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @vuepress/plugin-pwa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;.vuepress/config.js&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;&lt;span class="gd"&gt;- serviceWorker: true,
&lt;/span&gt;&lt;span class="gi"&gt;+ plugins: ['@vuepress/pwa']
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Instructions adapted from the &lt;a href="https://v1.vuepress.vuejs.org/plugin/official/plugin-pwa.html#migration-from-0-x"&gt;migration guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There are many new features that come with the &lt;code&gt;pwa&lt;/code&gt; plugin, which you can learn about in the &lt;a href="https://v1.vuepress.vuejs.org/plugin/official/plugin-pwa.html"&gt;pwa plugin docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Update styles
&lt;/h2&gt;

&lt;p&gt;VuePress allows you to apply &lt;a href="https://v1.vuepress.vuejs.org/config/#styling"&gt;custom styles&lt;/a&gt; to your website, which is great to use on-brand colors and global CSS.&lt;/p&gt;

&lt;p&gt;Previously, this could be done using &lt;code&gt;.vuepress/override.styl&lt;/code&gt; (for custom colors) and &lt;code&gt;.vuepress/style.styl&lt;/code&gt; (for arbitrary CSS).&lt;/p&gt;

&lt;p&gt;VuePress 1.x now looks for these in &lt;code&gt;.vuepress/styles/palette.styl&lt;/code&gt; and &lt;code&gt;.vuepress/styles/index.styl&lt;/code&gt; respectively (see &lt;a href="https://v1.vuepress.vuejs.org/miscellaneous/migration-guide.html#default-theme-config"&gt;migration guide&lt;/a&gt;). Upgrading is merely a matter of moving and renaming the existing files!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: custom themes can now also have their own copy of these, as described in &lt;a href="https://v1.vuepress.vuejs.org/theme/writing-a-theme.html#directory-structure"&gt;Writing a theme&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;The points above were already covered in the official migration guide. The rest of this post focuses on extra steps I had to follow to successfully upgrade the Bocadillo documentation to use VuePress 1.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Update component imports
&lt;/h2&gt;

&lt;p&gt;Another great feature of VuePress is that it allows you to &lt;a href="https://v1.vuepress.vuejs.org/guide/using-vue.html"&gt;use Vue components in Markdown&lt;/a&gt;, including registering your own components in &lt;code&gt;.vuepress/components&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The default theme comes with a bunch of these components which I had been reusing in custom components of mine.&lt;/p&gt;

&lt;p&gt;The file structure of the default theme changed, so I needed to fix the imports of these built-in components. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import NavLink from "@theme/NavLink.vue";
&lt;/span&gt;&lt;span class="gi"&gt;+ import NavLink from "@theme/components/NavLink.vue";
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I now keep an eye on the &lt;a href="https://github.com/vuejs/vuepress/tree/master/packages/%40vuepress/theme-default"&gt;&lt;code&gt;theme-default&lt;/code&gt;&lt;/a&gt; folder to see everything that can be imported using &lt;code&gt;@theme/*&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Fix custom layouts
&lt;/h2&gt;

&lt;p&gt;The default theme in VuePress allows you to use &lt;a href="https://v1.vuepress.vuejs.org/theme/default-theme-config.html#custom-layout-for-specific-pages"&gt;custom layout for specific pages&lt;/a&gt;, i.e. replace the Vue component responsible for rendering a page with a custom one. To do this, you create a Vue component in &lt;code&gt;.vuepress/components&lt;/code&gt; and specify the &lt;code&gt;layout&lt;/code&gt; option in the frontmatter.&lt;/p&gt;

&lt;p&gt;I had been using this for the layout of blog posts in the &lt;a href="https://bocadilloproject.github.io/news/"&gt;news&lt;/a&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docs/news/some-post.md&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;When I upgraded to 1.x, the layout was broken. I had various layout issues related to changes in the default CSS, but more specifically, the navbar wouldn't show up anymore!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iIWmHOyv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uw54ywlgd1bximslwup0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iIWmHOyv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uw54ywlgd1bximslwup0.png" alt="" width="880" height="492"&gt;&lt;/a&gt;&lt;/p&gt;
Ugh! The layout is off, and… where is my navbar?



&lt;p&gt;This is because &lt;strong&gt;the component given to &lt;code&gt;layout&lt;/code&gt; now completely replaces the page&lt;/strong&gt; (including the base &lt;code&gt;Layout&lt;/code&gt; component which contains the navbar, sidebar, etc.). Here, look at what &lt;a href="https://v1.vuepress.vuejs.org/theme/default-theme-config.html#custom-layout-for-specific-pages"&gt;the docs&lt;/a&gt; say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you wish to use &lt;strong&gt;a completely custom component in place of the page&lt;/strong&gt;, you can again specify the component to use using YAML front matter:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we want the base &lt;code&gt;&amp;lt;Layout&amp;gt;&lt;/code&gt; to be included again, we need to explicitly wrap the custom layout component in it, and use one of the available &lt;a href="https://vuejs.org/v2/guide/components-slots.html"&gt;slots&lt;/a&gt; (see &lt;a href="https://github.com/vuejs/vuepress/blob/master/packages/%40vuepress/theme-default/layouts/Layout.vue"&gt;Layout.vue&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So this is how &lt;code&gt;Post.vue&lt;/code&gt; now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Layout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"page-top"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- Page content… --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Layout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@theme/layouts/Layout.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I fixed this, the navbar was back!&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Consider theme inheritance
&lt;/h2&gt;

&lt;p&gt;VuePress comes with a built-in default theme, but previously customizing it was hard and you often had to &lt;strong&gt;eject&lt;/strong&gt;. It was impractical, because you now had a lot of files which wouldn't be updated with new versions of VuePress anymore.&lt;/p&gt;

&lt;p&gt;To fix this, VuePress 1.x comes with a brilliant new feature called &lt;a href="https://v1.vuepress.vuejs.org/theme/inheritance.html"&gt;Theme inheritance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In practice, this means we can build a custom theme that extends the default theme by creating a &lt;code&gt;.vuepress/theme/index.js&lt;/code&gt; file with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vuepress/default-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then &lt;a href="https://v1.vuepress.vuejs.org/theme/inheritance.html#override-components"&gt;override a particular set of components&lt;/a&gt; by placing them in &lt;code&gt;.vuepress/theme/components&lt;/code&gt;. They will be available under the &lt;code&gt;@theme&lt;/code&gt; alias just as if they came from the default theme!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: in addition to this, Vue itself also allows you to &lt;a href="https://alligator.io/vuejs/composing-components/#extends"&gt;extend components&lt;/a&gt;, which can be very useful to create a custom component based on a component included in the default theme (or other component, really).&lt;/p&gt;

&lt;p&gt;I didn't use this myself in the upgrade of the Bocadillo docs, so I can't go into more detail, but I thought this new feature was definitely worth sharing!&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Final checks
&lt;/h2&gt;

&lt;p&gt;Once you've been through the above steps, you should make sure your site runs and builds correctly before pushing it to production.&lt;/p&gt;

&lt;p&gt;For example, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;vuepress dev&lt;/code&gt; to start the dev server, and then go around to see if anything looks odd or broken.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;vuepress build&lt;/code&gt; to make sure that the build runs smoothly.&lt;/li&gt;
&lt;li&gt;Serve the built website to make sure there are no final cracks. I like to use Python for this: &lt;code&gt;$ python -m http.server -d path/to/.vuepress/dist&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This is it! Your VuePress website should now be upgraded to 1.x. Hopefully this was useful to you in upgrading from 0.x, and maybe you've learnt about the new features that landed in 1.x.&lt;/p&gt;

&lt;p&gt;If you're interested in what the upgrade looked like for me, you can take a look at the &lt;a href="https://github.com/bocadilloproject/bocadillo/pull/309/files"&gt;Pull Request&lt;/a&gt; for the Bocadillo docs site.&lt;/p&gt;

&lt;p&gt;Happy upgrading!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>vue</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Entropic: federated JS package registry, announced at JSConfEU 2019</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Wed, 05 Jun 2019 08:10:21 +0000</pubDate>
      <link>https://forem.com/florimondmanca/entropic-federated-js-package-registry-announced-at-jsconfeu-2019-3mmo</link>
      <guid>https://forem.com/florimondmanca/entropic-federated-js-package-registry-announced-at-jsconfeu-2019-3mmo</guid>
      <description>&lt;p&gt;Hey everyone! I don't think I've seen this discussed on DEV yet. I'm not the most aware about the JS community (I'm a Pythonista at the core), but I thought it'd be interesting to have a chat together about this.&lt;/p&gt;

&lt;p&gt;C J Silverio gave a talk at JSConfEU 2019 a few days ago:&lt;/p&gt;

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

&lt;p&gt;The talk is super interesting and very well crafted — I highly recommend you watch it! But here's a modest tl;dr.&lt;/p&gt;

&lt;p&gt;In this talk, Silverio discusses the reasons behind the Entropic project.&lt;/p&gt;

&lt;p&gt;The first reason is that a part of the "JavaScript commons" (the package registry) is owned by a private company (npm Inc), whose sole purpose is to "take money and turn it into more money".&lt;/p&gt;

&lt;p&gt;She makes the point that having &lt;em&gt;another&lt;/em&gt; private company (Microsoft) run the Node package ecosystem isn't going to be the solution either.&lt;/p&gt;

&lt;p&gt;(We previously discussed the GitHub Package Registry on DEV:)&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/peter" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1075%2F1c1975ce-97e8-401f-b99f-1ea88f9cae3e.jpeg" alt="peter"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/peter/github-announces-github-package-registry-3a27" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;GitHub announces GitHub Package Registry&lt;/h2&gt;
      &lt;h3&gt;Peter Kim Frank ・ May 10 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#news&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#npm&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#github&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Plus, still according to her, building any other &lt;em&gt;centralized&lt;/em&gt; package registry — even owned by a foundation like &lt;a href="https://openjsf.org/" rel="noopener noreferrer"&gt;OpenJS&lt;/a&gt; — wouldn't work either, because the flow of package downloads in the Node.js ecosystem is &lt;em&gt;humongous&lt;/em&gt;. It simply costs way too much to host and serve all packages from a central location.&lt;/p&gt;

&lt;p&gt;With all the reasons in mind, at the very end of the talk Silverio introduces &lt;strong&gt;Entropic&lt;/strong&gt;, a "federated package registry for anything, but mostly JavaScript". It comes with a CLI called &lt;code&gt;ds&lt;/code&gt; (like "delta entropy" — a of bit of a physics joke).&lt;/p&gt;

&lt;p&gt;From the words of C J Silverio herself, Entropic is still very young ("don't use it yet!"), but a handful of people are working on the Entropic project already. The main repository is here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&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%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/entropic-dev" rel="noopener noreferrer"&gt;
        entropic-dev
      &lt;/a&gt; / &lt;a href="https://github.com/entropic-dev/entropic" rel="noopener noreferrer"&gt;
        entropic
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🦝 📦 a package registry for anything, but mostly javascript 🦝 🦝 🦝
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Entropic: a federated package registry for anything&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/entropic-dev/entropic#contributors" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/86cdef7c44cb0bc1aebbafbf18fd1d36193342d9d05321a9f7380cbd608b7fe7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f616c6c5f636f6e7472696275746f72732d32332d6f72616e67652e7376673f7374796c653d666c61742d737175617265" alt="All Contributors"&gt;&lt;/a&gt; &lt;a href="https://github.com/entropic-dev/entropic./.github/CODE_OF_CONDUCT.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1ddea30a5a7e14223c2daaba0673bf8392db5f58bfa441c28c3b5937f8ef141e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2545322539442541342d636f64652532306f66253230636f6e647563742d626c75652e7376673f7374796c653d666c61742d737175617265" alt="Code of Conduct"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A new package registry with a new CLI, designed to be easy to stand up inside your network. Entropic features an entirely new file-centric API and a content-addressable storage system that attempts to minimize the amount of data you must retrieve over a network. This file-centric approach also applies to the publication API. See the &lt;a href="https://github.com/entropic-dev/entropic/tree/master/docs#apis" rel="noopener noreferrer"&gt;API section of the manifesto&lt;/a&gt; for more details about the API offered.&lt;/p&gt;
&lt;p&gt;Entropic assumes many registries co-existing and interoperating as a part of your normal workflow. All Entropic packages are namespaced, and a full Entropic package spec also includes the hostname of its registry.&lt;/p&gt;
&lt;p&gt;The legacy node package manager is treated as a read-only archive. You may install legacy packages through your Entropic home instance.&lt;/p&gt;
&lt;p&gt;See &lt;a href="https://github.com/entropic-dev/entropicdocs/README.md" rel="noopener noreferrer"&gt;docs/README.md&lt;/a&gt; for the manifesto.&lt;/p&gt;
&lt;p&gt;Are you interested in contributing? Do you have some feedback to share? Come talk with us in our…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/entropic-dev/entropic" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;So, &lt;strong&gt;what are your reactions to this announcement?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>discuss</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Attending Tech Conferences: A Beginner’s Guide</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Wed, 29 May 2019 16:02:55 +0000</pubDate>
      <link>https://forem.com/florimondmanca/attending-tech-conferences-a-beginner-s-guide-45hp</link>
      <guid>https://forem.com/florimondmanca/attending-tech-conferences-a-beginner-s-guide-45hp</guid>
      <description>&lt;p&gt;&lt;a href="https://pyconweb.com"&gt;PyConWeb&lt;/a&gt; is a conference focusing on Python, web development, and related technologies. The 2019 edition took place on the 25th and 26th of May in Munich — and I was there! 🙌&lt;/p&gt;

&lt;p&gt;Going to this conference was very special for me. It was the first time I had &lt;strong&gt;attended a tech conference&lt;/strong&gt;, but also &lt;strong&gt;given a tech talk&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;The talk I gave was about &lt;a href="https://bocadilloproject.github.io"&gt;Bocadillo&lt;/a&gt;, async Python for web development, and open source, and lasted 30 minutes. It was a great opportunity to explain the concepts behind ASGI, highlight some key Bocadillo features, and share what I'v learnt about maintaining open source projects. All in a 10-question long &lt;em&gt;"Q&amp;amp;A with myself"&lt;/em&gt; format. If you're interested, the slides are available &lt;a href="https://github.com/florimondmanca/talks/blob/master/2019_05_25-bocadillo_pyconweb2019.pdf"&gt;on GitHub&lt;/a&gt;. ✨&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6oQ9O62Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/b0eb426c-9a44-4381-8370-afbf4bd24964.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6oQ9O62Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/b0eb426c-9a44-4381-8370-afbf4bd24964.png" alt="" width="500" height="279"&gt;&lt;/a&gt;&lt;/p&gt;
Title slide!



&lt;p&gt;In this post, I want to share with you &lt;strong&gt;what I've learnt about attending tech conferences&lt;/strong&gt;, and give away &lt;strong&gt;actionable tips&lt;/strong&gt; if you plan to attend yourself!&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a tech conference?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;conference&lt;/strong&gt; is an event in which professionals and hobbyists interested in a particular topic gather to learn, train, and share ideas about this topic.&lt;/p&gt;

&lt;p&gt;There are conferences for many different types of audiences, such as researchers, business people, engineers, etc. Many different activities take place, but &lt;strong&gt;talks&lt;/strong&gt; and &lt;strong&gt;workshops&lt;/strong&gt; are the most common.&lt;/p&gt;

&lt;p&gt;Even among tech conferences though, &lt;strong&gt;a vast amount of topics exist&lt;/strong&gt;. Conferences are often organised around a technology (a programming language, a framework, …) or field (frontend development, security, etc.). For example, PyConWeb is "a conference about web tools in Python".&lt;/p&gt;

&lt;h3&gt;
  
  
  Why should I attend?
&lt;/h3&gt;

&lt;p&gt;There are probably as many reasons for going to tech conferences as there are people attending them. So my answer would be that &lt;strong&gt;it depends on you&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Do you want to &lt;strong&gt;learn&lt;/strong&gt; about a particular topic, get involved in &lt;strong&gt;networking&lt;/strong&gt; (i.e. meeting and connecting with new people), &lt;strong&gt;find a job&lt;/strong&gt;, or simply &lt;strong&gt;have a good time&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Most of the time though, I'd say it's &lt;strong&gt;a combination of these&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Personally, I went to PyConWeb first and foremost because I was giving a talk there (very good reason on its own, right?), but also because I like Python and web development and I was curious to know what a conference on this topic would look like. To be honest, I didn't really know what to expect, but in the end I learnt a lot and got to meet a lot of new friendly people.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to be an expert to attend conferences?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Absolutely not!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me reiteriate, as I think this is a very important point: &lt;strong&gt;everyone belongs to conferences!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's no such thing as "qualifying" or "not qualifying" for a conference. &lt;strong&gt;Everyone is welcome&lt;/strong&gt; regardless of their technical level or knowledge of the field. You're here to learn, after all!&lt;/p&gt;

&lt;p&gt;I met people at PyConWeb 2019 who were getting started with or transitioning into Python and/or web development, and also experts in more specific domains.&lt;/p&gt;

&lt;p&gt;The people is what makes attending conferences so uniquely fun, so join and be part of it!&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I find conferences?
&lt;/h3&gt;

&lt;p&gt;At first, it may not be obvious where you should look for the upcoming events.&lt;/p&gt;

&lt;p&gt;One easy way to find them is to simply ask a search engine. For example, making a search for "python conferences" yields results about conferences around me (e.g. PyConFR), but also websites that list such conferences, such as &lt;a href="https://python.org/community/workshops"&gt;python.org&lt;/a&gt;, &lt;a href="https://pycon.org"&gt;pycon.org&lt;/a&gt; or &lt;a href="https://confs.tech/python"&gt;confs.tech&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Twitter is also a great way to hear about upcoming conferences. Just search for "conf", and you'll find tons of official conferences accounts to look at!&lt;/p&gt;

&lt;h3&gt;
  
  
  Deciding on a conference to attend
&lt;/h3&gt;

&lt;p&gt;Since there is a lot of possible conferences you can attend, which one(s) should you choose?&lt;/p&gt;

&lt;p&gt;I'd say that the following criteria are enough to help you decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Topic&lt;/strong&gt;: it may be obvious, but select a conference which you will be interested in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location&lt;/strong&gt;: finding a conference near your place is probably best at first, but you may also want to take the opportunity to travel in another city or country.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size / popularity&lt;/strong&gt;: both smaller and larger conferences have their pro's and con's. Small to medium conferences (like PyConWeb) feel more like a family reunion, while larger conferences obviously tend to attract very notorious speakers and shiny sponsors, but can be harder to navigate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price&lt;/strong&gt;: some conferences are more expansive than others — even though it does not necessarily reflect the quality of the event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dates&lt;/strong&gt;: again, it may be obvious, but be sure that you will be available to attend. If you're not, no worries — conferences happen at all times of the year!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may also want to take a look at testimonials, and what people said about the previous editions.&lt;/p&gt;

&lt;p&gt;It's now time to dig into more specific tips about &lt;strong&gt;what to do before, during and after the conference&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Before the conference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Getting your ticket
&lt;/h3&gt;

&lt;p&gt;So you've chosen a conference, and you're excited to go — nice! It is now time to &lt;strong&gt;get your ticket&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Indeed, &lt;strong&gt;in general conferences are not free&lt;/strong&gt;. From what I've seen, ticket prices range from 60-70€ for the smaller conferences up to a few hundred € for the largest events.&lt;/p&gt;

&lt;p&gt;There are actually many costs that come with organizing a conference, including paying for the venue, logistics, or security. Many conferences have &lt;strong&gt;sponsors&lt;/strong&gt; though, which helps them reduce the price.&lt;/p&gt;

&lt;p&gt;If you can't afford the price yourself, it's probably a good idea to investigate &lt;strong&gt;whether your employer could help&lt;/strong&gt;, e.g. via a yearly self-learning budget.&lt;/p&gt;

&lt;p&gt;If you're a &lt;strong&gt;student&lt;/strong&gt;, many conferences have special student tickets which are often less expensive.&lt;/p&gt;

&lt;p&gt;At PyConWeb, I paid about 45€ for the entry, which is a 90€ student ticket with a 50% speaker discount. The entry ticket was all-inclusive: talks, workshops, and food and beverages. This may not be the case for all conferences, though. In particular, some conferences may charge extra for workshops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logistics
&lt;/h3&gt;

&lt;p&gt;Depending on the location, you may need to book &lt;strong&gt;accommodation and transportation&lt;/strong&gt;, all of which come at a price as well.&lt;/p&gt;

&lt;p&gt;PyConWeb took place in Munich, so I had to travel from Paris and book a place to stay. This represents about 200€ on transportation (train and metro fares) and about 150€ on accommodation.&lt;/p&gt;

&lt;p&gt;There are often many possible options, and I recommend taking the time to &lt;strong&gt;compare prices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's best to figure out the logistics &lt;strong&gt;one week before the conference&lt;/strong&gt;, so that you arrive at the conference as relaxed as possible, ready to enjoy your time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;Even though you're not giving a talk, in my opinion &lt;strong&gt;attending a conference requires some preparation&lt;/strong&gt; in order to get the most out of it.&lt;/p&gt;

&lt;p&gt;A few days before the conference, it's a good idea to &lt;strong&gt;skim through the list of talks and workshops&lt;/strong&gt;, and see which ones you may want to attend.&lt;/p&gt;

&lt;p&gt;The day before the conference, the PyConWeb team had setup a &lt;strong&gt;mobile web app&lt;/strong&gt;, with the schedule, speakers bios and practical information listed. I could fav the talks there, and learn about the other speakers' backgrounds. Not all conferences provide an app, but if they do I highly recommend using it!&lt;/p&gt;

&lt;p&gt;The PyConWeb team also had a &lt;strong&gt;discussion chat&lt;/strong&gt;. If the conference you go to has one as well, join and say hi! It's a good way to introduce yourself and see who else will be there.&lt;/p&gt;

&lt;h2&gt;
  
  
  During the conference
&lt;/h2&gt;

&lt;p&gt;Now that you've arrived at the conference, what should you do? Let's take a closer look!&lt;/p&gt;

&lt;h3&gt;
  
  
  Talks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Talks&lt;/strong&gt; are without a doubt the highlight of any conference. Speakers come from all around the world to talk about their experience, share ideas, and make sure the audience enjoys their time. Talks are selected by conference organisers to be as &lt;strong&gt;diverse and interesting&lt;/strong&gt; as possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mo9wpujH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/9ca7c663-1d90-4377-a043-7ec8cbb22f80.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mo9wpujH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/9ca7c663-1d90-4377-a043-7ec8cbb22f80.JPG" alt="" width="423" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
Printed schedule for the morning of May 25th at PyConWeb 2019.



&lt;p&gt;All that said, &lt;strong&gt;attending all the talks is generally impossible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most conferences have more than one track of talks, which means that two or more talks take place at the same time. As a result, you'll have to decide which talk you want to attend.&lt;/p&gt;

&lt;p&gt;Some conferences &lt;strong&gt;record talks&lt;/strong&gt; and make them available online afterwards — if that's the case, you can be looser on which talks to decide to see.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;which talks should you prioritise?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think you should primarily attend talks about &lt;strong&gt;topics you don't know&lt;/strong&gt;, or topics you know about that are &lt;strong&gt;presented in an original way&lt;/strong&gt;. After all, you're most likely here to learn, right?&lt;/p&gt;

&lt;p&gt;Also, and this may be counter-intuitive, I think you should go to at least &lt;strong&gt;one talk that you think won't be interesting to you&lt;/strong&gt;, or that doesn't sound very sexy. You may be surprised about what you discover, and will help fill what could have otherwise been an empty room (which is probably a speaker's worst fear).&lt;/p&gt;

&lt;p&gt;During a talk, I highly recommend you &lt;strong&gt;take notes&lt;/strong&gt; about key points or questions you may want to ask. Personally, it helps me memorise key learning points better. If you do so, prefer using &lt;strong&gt;pen and paper&lt;/strong&gt; instead of your laptop, as it is less intrusive for both the speaker and other attendees.&lt;/p&gt;

&lt;p&gt;If the talk turns out to be a bit boring, &lt;strong&gt;refrain from zoning out&lt;/strong&gt; and starting browsing your phone. It can be disheartening to the speaker, make them even less at ease and worsen the experience for everyone. Instead, take it as an opportunity to observe what makes the talk boring.&lt;/p&gt;

&lt;p&gt;After the talk, you can (should?) &lt;strong&gt;reach out to the speaker&lt;/strong&gt; if you enjoyed the talk, and tell them why you found it interesting. Speakers are always up for feedback and ways they can improve! Obviously, you can also ask them anything unclear or you'd like more information about.&lt;/p&gt;

&lt;p&gt;In the hallway, you can also &lt;strong&gt;discuss the talk&lt;/strong&gt; you've just seen with other attendees, or ask those who went to other talks what they thought about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other activities
&lt;/h3&gt;

&lt;p&gt;Although talks are the main activity at most conferences, there are many &lt;strong&gt;other activities&lt;/strong&gt; going on.&lt;/p&gt;

&lt;p&gt;One of these activities is &lt;strong&gt;workshops&lt;/strong&gt;. They're typically longer than talks (2 to 3 hours) and consist in a hands-on session about a tool or technology.&lt;/p&gt;

&lt;p&gt;For example, I decided to attend a workshop by Ben Parsons about real-time asynchronous messaging with &lt;a href="https://matrix.org"&gt;Matrix&lt;/a&gt;, and I enjoyed it a lot!&lt;/p&gt;

&lt;p&gt;Another activity organised at PyCon events is &lt;strong&gt;lightning talks&lt;/strong&gt;. They consist of very short 5-minutes talks which anyone can give, and they're often very fun to attend.&lt;/p&gt;

&lt;p&gt;There may also be &lt;strong&gt;sponsor booths&lt;/strong&gt; in the hall. It can be a good idea to go and say hi, e.g. if you're interested in career opportunities or just to get some &lt;strong&gt;sponsor swag&lt;/strong&gt; (stickers, bookmarks, etc).&lt;/p&gt;

&lt;p&gt;Lastly, be sure to attend the &lt;strong&gt;conference opening&lt;/strong&gt; and &lt;strong&gt;conference announcements&lt;/strong&gt;. They're definitely not as boring as they seem (bad jokes inside!), and organizers may announce fun special contests or events which you can participate in. For example, PyConWeb had a selfie contest going on, as well as an open brainstorming session for the name of The Cat, the conference's mascot (which was finally elected to be &lt;code&gt;CharField&lt;/code&gt;!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pzeMpkmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/D7lupfaXsAETYHe.jpg:large" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pzeMpkmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/D7lupfaXsAETYHe.jpg:large" alt="" width="880" height="589"&gt;&lt;/a&gt;&lt;/p&gt;
PyConWeb 2019 swag, featuring CharField himself. 🐈



&lt;h3&gt;
  
  
  Social media
&lt;/h3&gt;

&lt;p&gt;If you're into social media, conferences are an incredible source of inspiration for posts and Tweets. As a matter of fact, people are typically &lt;strong&gt;more active than usual&lt;/strong&gt; during a conference, so don't be shy!&lt;/p&gt;

&lt;p&gt;During my talk, some attendees tweeted — I had placed my Twitter handle in the footer of each slide — and it was great to see, because it probably meant that they thought &lt;strong&gt;the talk was worth sharing&lt;/strong&gt;. I also tweeted a lot about other people's talks!&lt;/p&gt;

&lt;p&gt;By the way, it's definitely okay to get your phone out, take a picture, and publish something about the talk. But be sure to do it in a way that doesn't prevent other attendees from enjoying the talk. Try to do it quickly, or you risk zoning out and missing on some interesting content. To avoid this entirely, you can also take a picture and write the post after the talk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Networking
&lt;/h3&gt;

&lt;p&gt;Attending a conference is a fantastic opportunity to &lt;strong&gt;meet and connect with new people&lt;/strong&gt;. In fact, it's probably the main motivation for many attendees. I was very impressed myself about how many new people I met at PyConWeb!&lt;/p&gt;

&lt;p&gt;Meeting new people turned out to be very easy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify someone or a group of people you don't know yet.&lt;/li&gt;
&lt;li&gt;Say hi, tell your name, shake hands.&lt;/li&gt;
&lt;li&gt;Strike a conversation!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I think this was even easier since I came by myself at the conference. I know I would've been tempted to stick with my team or colleagues if I came with other people.&lt;/p&gt;

&lt;p&gt;Once you're in, remember to encourage the &lt;strong&gt;PacMan shape&lt;/strong&gt;. In other words, leave a small gap open instead of forming a closed circle. It shows to people looking for a group that they are &lt;strong&gt;welcome to join&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're unsure about what to talk about, there are actually &lt;strong&gt;many possible topics&lt;/strong&gt;. For example, you can ask other people:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What motivated them to come to the conference.&lt;/li&gt;
&lt;li&gt;How they relate to the topic of the conference. At PyConWeb, I enjoyed asking other people what web frameworks they used, or what they used Python for and how they got into it.&lt;/li&gt;
&lt;li&gt;Where they're traveling from, how long they plan to stay.&lt;/li&gt;
&lt;li&gt;Which conferences or events they've been to in the past.&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list is endless! Just listen and bounce off the answers. Also, be ready to answer those questions yourself!&lt;/p&gt;

&lt;p&gt;Another great thing about conferences is that a lot of them are &lt;strong&gt;international&lt;/strong&gt;. I obviously met attendees who were from Munich or Germany, but also people from Canada, Portugal, the UK, Singapore, India, and Slovenia!&lt;/p&gt;

&lt;p&gt;Note that if you're a local but the conference is international, the language for talks and hallway chit-chat will probably be English.&lt;/p&gt;

&lt;p&gt;As a final note, be aware that &lt;strong&gt;networking and socialising can be exhausting&lt;/strong&gt;, especially if, like me, you are a bit of an introvert.&lt;/p&gt;

&lt;p&gt;Actually, you shouldn't feel obliged to be constantly talking to people. It's okay to &lt;strong&gt;take a break&lt;/strong&gt;, sit somewhere alone, and &lt;strong&gt;recharge your batteries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Everyone has their limits, so remember to &lt;strong&gt;take care of yourself&lt;/strong&gt; so that the conference remains a memorable experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Behavior
&lt;/h3&gt;

&lt;p&gt;A lot of conferences have a &lt;strong&gt;code of conduct&lt;/strong&gt;, which is a short document that frames how people should behave during the conference.&lt;/p&gt;

&lt;p&gt;The PyConWeb code of conduct is a few pages long, but the tl;dr which was given to us at the conference opening was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We're all adults. Be nice to people, and expect the same in return.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After all, a conference is a &lt;strong&gt;professional event&lt;/strong&gt;, which means you're supposed to behave as a professional.&lt;/p&gt;

&lt;p&gt;That doesn't mean it has to be &lt;em&gt;formal&lt;/em&gt; — you can totally have &lt;strong&gt;informal and fun discussions&lt;/strong&gt; with speakers and other attendees. The Saturday night party was a good example of this!&lt;/p&gt;

&lt;p&gt;But while doing so, be sure to stay respectful, open-minded and inclusive.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the conference
&lt;/h2&gt;

&lt;p&gt;When the official program is over, there are still many ways you can keep your conference experience going.&lt;/p&gt;

&lt;p&gt;First and foremost, &lt;strong&gt;say thanks&lt;/strong&gt; to the organizers and the crew! They worked hard to organize the conference and make sure everything goes smoothly. You know they succeeded in doing so when you can't even notice the work they've done before or during the conference. So reach out to them, and say thanks!&lt;/p&gt;

&lt;p&gt;For example: did you like the selection of talks? Was the schedule well-managed? Did you enjoy the extra activities? &lt;strong&gt;Tell them!&lt;/strong&gt; You can do it in many ways: in person, on social media, in the chat, etc.&lt;/p&gt;

&lt;p&gt;Another thing you can do is &lt;strong&gt;reach out to people you met during the conference&lt;/strong&gt;. Thank them for your discussions, offer to meet again sometime, and follow up on conversations you've had. A nice time to do this is after the final round of talks.&lt;/p&gt;

&lt;p&gt;If you've exchanged contact info (Twitter, LinkedIn, phone, email…) you can also &lt;strong&gt;stay in touch&lt;/strong&gt; after everyone went home.&lt;/p&gt;

&lt;p&gt;Outside of extended networking, there are things you can do on your own.&lt;/p&gt;

&lt;p&gt;For example, try to &lt;strong&gt;apply one or two things you've learnt during the conference&lt;/strong&gt; to a practical project. If you do, remember to share it with the speaker!&lt;/p&gt;

&lt;p&gt;For example, I created &lt;a href="https://github.com/florimondmanca/fetch-metadata-asgi"&gt;fetch-metadata-asgi&lt;/a&gt;, a PoC ASGI middleware for the upcoming Fetch Metadata specification. This was a &lt;a href="https://twitter.com/florimondmanca/status/1132565224450592768?s=12"&gt;follow up&lt;/a&gt; on the talk given by Lukas Weichselbaum about new web security features.&lt;/p&gt;

&lt;p&gt;Lastly, it's definitely a good idea to take the time to &lt;strong&gt;reflect on your experience&lt;/strong&gt;. If you have a blog, you can write a blog post and share it with attendees and organizers. Examples from PyConWeb 2019:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This very blog post!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jugmac00.github.io/blog/pyconweb-2019/"&gt;Came for the talks, stayed for the raffles&lt;/a&gt; by Jürgen Gmach — great summary with links to slides and source code for the talks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.operun.de/blog/pyconweb-2019"&gt;PyConWeb 2019&lt;/a&gt; by Jesse Stippel.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;PyConWeb 2019 was full of first times for me: first time in Germany and in Munich, first time attending a tech conference, and first time giving a talk at a conference!&lt;/p&gt;

&lt;p&gt;I genuinely enjoyed it. It made me consider getting more into public speaking, and attending more conferences — from the Python community in particular. I just learnt that &lt;a href="https://twitter.com/pyconfr/status/1133004539018268672"&gt;PyConFR 2019 was confirmed&lt;/a&gt; for October, so that might be my next stop!&lt;/p&gt;

&lt;p&gt;I will be writing up my experience of speaking at PyConWeb in an upcoming blog post, in a similar format: &lt;em&gt;"Speaking at Tech Conferences: A Beginner's Guide"&lt;/em&gt;. Stay tuned, and in the meantime you can view the slides for my talk &lt;a href="https://github.com/florimondmanca/talks/blob/master/2019_05_25-bocadillo_pyconweb2019.pdf"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>techtalks</category>
    </item>
    <item>
      <title>Building a real-time chatbot server in Python with WebSocket, ChatterBot and Bocadillo</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Tue, 19 Mar 2019 09:15:18 +0000</pubDate>
      <link>https://forem.com/bocadillo/building-a-real-time-chatbot-server-in-python-with-websocket-chatterbot-and-bocadillo-482g</link>
      <guid>https://forem.com/bocadillo/building-a-real-time-chatbot-server-in-python-with-websocket-chatterbot-and-bocadillo-482g</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is an adaptation of the &lt;a href="https://bocadilloproject.github.io/getting-started/tutorial.html" rel="noopener noreferrer"&gt;official Bocadillo tutorial&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hi everyone! Today's post is going to be a little special. Some of you may remember this post I wrote some months back: &lt;a href="https://dev.to/florimondmanca/how-i-built-a-python-web-framework-and-became-an-open-source-maintainer-3okd"&gt;How I Built A Python Web Framework And Became An Open Source Maintainer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since then, I kept working on &lt;a href="https://github.com/bocadilloproject/bocadillo" rel="noopener noreferrer"&gt;Bocadillo&lt;/a&gt;, and it's been a great time! In fact, I learnt just last week that &lt;strong&gt;I will be flying to Munich at the end of May to give a talk at &lt;a href="https://pyconweb.com" rel="noopener noreferrer"&gt;PyConWeb 2019&lt;/a&gt;!&lt;/strong&gt; This will be my first conference and talk ever, so needless to say that I'm SUPER EXCITED! 🙌🤩&lt;/p&gt;

&lt;p&gt;Another great news is that &lt;strong&gt;Bocadillo v0.13 has just been released&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1107029038923743232-902" src="https://platform.twitter.com/embed/Tweet.html?id=1107029038923743232"&gt;
&lt;/iframe&gt;

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



&lt;/p&gt;

&lt;p&gt;With all these good vibes in the air, I finally decided to go ahead and publish a thorough tutorial.&lt;/p&gt;

&lt;p&gt;Without further ado, here's the plot: we're going to try and build a &lt;strong&gt;chatbot server&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Bocadillo has many features built-in, so this is a great opportunity to go through some aspects of building web services with Bocadillo.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll get to play with chatbots but also learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;WebSocket&lt;/strong&gt; to handle multiple connections in real-time.&lt;/li&gt;
&lt;li&gt;Create REST endpoints.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;providers&lt;/strong&gt; to inject reusable resources into views.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt; a Bocadillo application using &lt;a href="https://docs.pytest.org" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still wondering how we'll build something seemingly as complex as a chatbot? Well, you probably know that Python has a &lt;em&gt;gigantic&lt;/em&gt; data science ecosystem. I would've bet actual money there would be a chatbot framework somewhere out there.&lt;/p&gt;

&lt;p&gt;Turns out — there was! After some research, I stumbled upon &lt;a href="https://github.com/gunthercox/ChatterBot" rel="noopener noreferrer"&gt;ChatterBot&lt;/a&gt;. It looks pretty solid and popular, so we'll use it to build &lt;strong&gt;Diego&lt;/strong&gt;, a friendly conversational agent. Don't worry, this won't require &lt;em&gt;any&lt;/em&gt; background in data science nor chatbot technology!&lt;/p&gt;

&lt;p&gt;Sounds exciting? Alright, let's dive in! 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;First things first: let's set up our project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open up a terminal, and create an empty directory somewhere on your computer, then &lt;code&gt;cd&lt;/code&gt; to it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/dev/bocadillo-chatbot
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev/bocadillo-chatbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Bocadillo and ChatterBot. We're using &lt;a href="https://pipenv.readthedocs.io" rel="noopener noreferrer"&gt;pipenv&lt;/a&gt; to install dependencies here, but you can also use plain ol' &lt;code&gt;pip&lt;/code&gt; + &lt;code&gt;virtualenv&lt;/code&gt; too.&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Note: pytz is required by chatterbot.&lt;/span&gt;
pipenv &lt;span class="nb"&gt;install &lt;/span&gt;bocadillo chatterbot pytz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create an empty &lt;code&gt;app.py&lt;/code&gt; script. This is where we'll create the application later on:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should now have the following directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Pipfile
├── Pipfile.lock
└── app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bootstrapping the application
&lt;/h2&gt;

&lt;p&gt;Now, let's write the app skeleton in &lt;code&gt;app.py&lt;/code&gt;. Hang tight — first decent bit of code incoming:&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;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If you've ever worked with Flask or, well, nearly any Python web framework really, this should look oddly familiar. Nearly boring. Who cares? It works! Check for yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you go to &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; and get a &lt;code&gt;404 Not Found&lt;/code&gt; response, you're all good! Enter &lt;code&gt;Ctrl+C&lt;/code&gt; in your terminal to stop the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the WebSocket endpoint
&lt;/h2&gt;

&lt;p&gt;We're now ready to get to the meat of it! The first thing we'll build is the &lt;strong&gt;WebSocket endpoint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're not familiar with WebSocket, don't worry — here's a 10-word summary: it allows a server and a client to exchange messages in a bidirectional way. It's good old sockets reinvented for the web.&lt;/p&gt;

&lt;p&gt;Due to their &lt;strong&gt;bidirectional nature&lt;/strong&gt;, they're very suitable for the kind of application we're building here — some sort of &lt;em&gt;conversation&lt;/em&gt; between a client and a server (i.e. our chatbot).&lt;/p&gt;

&lt;p&gt;If you're interested in learning more about WebSockets in Python, I strongly recommend this talk: &lt;a href="https://www.youtube.com/watch?v=PjiXkJ6P9pQ&amp;amp;frags=pl%2Cwn" rel="noopener noreferrer"&gt;A beginner's guide to WebSockets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Alright, so we won't plug the chatbot in yet. Instead, let's make the server send back any message it receives — a behavior also known as an "echo" endpoint.&lt;/p&gt;

&lt;p&gt;Add the following between the &lt;code&gt;app&lt;/code&gt; object declaration and the &lt;code&gt;app.run()&lt;/code&gt; block in &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.websocket_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few minimal explanations here, for the curious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This defines a WebSocket endpoint which will be accessible at the &lt;code&gt;ws://localhost:8000/conversation&lt;/code&gt; location.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;async for message in ws:&lt;/code&gt; line iterates over messages received over the WebSocket.&lt;/li&gt;
&lt;li&gt;Lastly, &lt;code&gt;await ws.send(message)&lt;/code&gt; sends the received &lt;code&gt;message&lt;/code&gt; as-is back to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trying out the WebSocket endpoint
&lt;/h2&gt;

&lt;p&gt;How about we try this out by creating a WebSocket client? Fear not — we won't need to write any JavaScript. We'll stick to Python and use the &lt;a href="https://websockets.readthedocs.io" rel="noopener noreferrer"&gt;websockets&lt;/a&gt; library, which comes installed with Bocadillo.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;client.py&lt;/code&gt; file and paste the following code there. What it does is connect to the WebSocket endpoint and run a simple REPL:&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;# client.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;suppress&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;websockets&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="n"&gt;url&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;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&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;gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# See asyncio docs for the Python 3.6 equivalent to .run().
&lt;/span&gt;    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ws://localhost:8000/conversation&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;Run the server-side application with &lt;code&gt;python app.py&lt;/code&gt; and, in a separate terminal, start the &lt;code&gt;client.py&lt;/code&gt; script. You should be greeted with a &lt;code&gt;&amp;gt;&lt;/code&gt; prompt. If so, start chatting!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python client.py
&amp;gt; Hi!
Hi!
&amp;gt; Is there anyone here?
Is there anyone here?
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty cool, isn't it? 🤓&lt;/p&gt;

&lt;p&gt;Type &lt;code&gt;Ctrl+C&lt;/code&gt; to exit the session and close the WebSocket connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello, Diego!
&lt;/h2&gt;

&lt;p&gt;Now that we're able to make the server and a client communicate, how about we replace the echo implementation with an actual, intelligent and friendly chatbot?&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://github.com/gunthercox/ChatterBot" rel="noopener noreferrer"&gt;ChatterBot&lt;/a&gt; comes in! We'll create a chatbot rightfully named &lt;strong&gt;Diego&lt;/strong&gt; — a chatbot speaking the asynchronous salsa. 🕺&lt;/p&gt;

&lt;p&gt;Go ahead and create a &lt;code&gt;chatbot.py&lt;/code&gt; file, and add Diego in there:&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;# chatbot.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatterbot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatBot&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatterbot.trainers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatterBotCorpusTrainer&lt;/span&gt;

&lt;span class="n"&gt;diego&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Diego&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatterBotCorpusTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chatterbot.corpus.english.greetings&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;chatterbot.corpus.english.conversations&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;(ChatterBot's chatbots are quite dumb out of the box, so the code above trains Diego on an English corpus to make him a bit smarter.)&lt;/p&gt;

&lt;p&gt;At this point, you can try out the chatbot in a Python interpreter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatbot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;  &lt;span class="c1"&gt;# Be patient — this may take a few seconds to load!
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hi, there!&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;Statement&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;There&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;preferably&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;obvious&lt;/span&gt; &lt;span class="n"&gt;way&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;do&lt;/span&gt; &lt;span class="n"&gt;it&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;(Hmm. Interesting response! 🐍)&lt;/p&gt;

&lt;p&gt;Let's now plug Diego into the WebSocket endpoint: each time we receive a new &lt;code&gt;message&lt;/code&gt;, we'll give it to Diego and send his response back.&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;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatbot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;

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

&lt;span class="nd"&gt;@app.websocket_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&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="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run the server/client setup from earlier, you can now see that Diego converses with us over the WebSocket!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python client.py
&amp;gt; Hi there!
I am a chat bot. I am the original chat bot. Did you know that I am incapable of error?
&amp;gt; Where are you?
I am on the Internet.
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like Diego is a jokester. 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring the chatbot as a provider
&lt;/h2&gt;

&lt;p&gt;Clients are now able to chat with Diego over a WebSocket connection. That's great!&lt;/p&gt;

&lt;p&gt;However, there are a few non-functional issues with our current setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading Diego is quite expensive: it takes about ten seconds on a regular laptop.&lt;/li&gt;
&lt;li&gt;Because of the &lt;code&gt;import&lt;/code&gt; at the top of the script, we'd load Diego every time we import the &lt;code&gt;app&lt;/code&gt; module. Not great!&lt;/li&gt;
&lt;li&gt;Diego is injected as a global dependency into the WebSocket endpoint: we can't swap it with another implementation (especially useful during tests), and it's not immediately clear that the endpoint depends on it at first sight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you think about it, Diego is a &lt;strong&gt;resource&lt;/strong&gt; — ideally, it should only be made available to the WebSocket endpoint at the time of processing a connection request.&lt;/p&gt;

&lt;p&gt;So, there must be a better way… and there is: &lt;a href="https://bocadilloproject.github.io/guides/injection/" rel="noopener noreferrer"&gt;providers&lt;/a&gt;. ✨&lt;/p&gt;

&lt;p&gt;Providers are a unique feature of Bocadillo. They were inspired by &lt;a href="https://docs.pytest.org/en/latest/fixture.html" rel="noopener noreferrer"&gt;pytest fixtures&lt;/a&gt; and offer an elegant, modular and flexible way to &lt;strong&gt;manage and inject resources into web views&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's use them to fix the code, shall we?&lt;/p&gt;

&lt;p&gt;First, let's move Diego to a &lt;code&gt;providerconf.py&lt;/code&gt; script:&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;# providerconf.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatterbot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatBot&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chatterbot.trainers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatterBotCorpusTrainer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;

&lt;span class="nd"&gt;@provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;diego&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Diego&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatterBotCorpusTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chatterbot.corpus.english.greetings&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;chatterbot.corpus.english.conversations&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above declares a &lt;code&gt;diego&lt;/code&gt; provider which we can now &lt;strong&gt;inject&lt;/strong&gt; into the WebSocket view. All we have to do is declare it as a &lt;strong&gt;parameter&lt;/strong&gt; to the view.&lt;/p&gt;

&lt;p&gt;Let's do just that by updating the &lt;code&gt;app.py&lt;/code&gt; script. Here, you get it in full:&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;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.websocket_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- 👋, Diego!
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&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="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;No imports required — Diego will &lt;em&gt;automagically&lt;/em&gt; get injected in the WebSocket view when processing the WebSocket connection request. ✨&lt;/p&gt;

&lt;p&gt;Alright, ready to try things out?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the &lt;code&gt;app.py&lt;/code&gt; script. You should see additional logs corresponding to Bocadillo setting up Diego on startup:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python app.py
INFO: Started server process &lt;span class="o"&gt;[&lt;/span&gt;29843]
INFO: Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data] Downloading package averaged_perceptron_tagger to
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]     /Users/Florimond/nltk_data...
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]   Package averaged_perceptron_tagger is already up-to-
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]       &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data] Downloading package punkt to /Users/Florimond/nltk_data...
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]   Package punkt is already up-to-date!
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data] Downloading package stopwords to
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]     /Users/Florimond/nltk_data...
&lt;span class="o"&gt;[&lt;/span&gt;nltk_data]   Package stopwords is already up-to-date!
Training greetings.yml: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;####################] 100%&lt;/span&gt;
Training conversations.yml: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;####################] 100%&lt;/span&gt;
INFO: Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run the &lt;code&gt;client.py&lt;/code&gt; script, and start chatting! You shouldn't see any difference from before. In particular, Diego responds just as fast.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python client.py
&amp;gt; Hello!
Hi
&amp;gt; I would like to order a sandwich
Yes it is.
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There you go! Beautiful, modular and flexible &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; with Bocadillo providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping track of clients
&lt;/h2&gt;

&lt;p&gt;Let's go one step further. True, we have quite elegantly implemented conversation with a chatbot over WebSocket. Now, how about we keep track of how many clients are currently talking to the chatbot?&lt;/p&gt;

&lt;p&gt;If you were wondering — yes, we can implement this with providers too!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's add a &lt;code&gt;clients&lt;/code&gt; provider to &lt;code&gt;providerconf.py&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# providerconf.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;

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

&lt;span class="nd"&gt;@provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now, let's add another provider which returns a context manager that takes care of registering the &lt;code&gt;ws&lt;/code&gt; connection to the set of clients. FYI, this is an example of a &lt;a href="https://bocadilloproject.github.io/guides/injection/factory.html" rel="noopener noreferrer"&gt;factory provider&lt;/a&gt;, but you don't really need to understand the whole code at this point.&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# providerconf.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@provider&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;clients&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;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&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;_register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In the WebSocket view, use the new &lt;code&gt;save_client&lt;/code&gt; provider to register the WebSocket client:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app.websocket_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;save_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&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="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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 it! While the client is chatting with Diego, it will be present in the set of &lt;code&gt;clients&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How about we do something with this information?&lt;/p&gt;

&lt;h2&gt;
  
  
  Exposing client count via a REST endpoint
&lt;/h2&gt;

&lt;p&gt;As a final feature, let's step aside from WebSocket for a moment and go back to the good old HTTP protocol. We'll create a simple REST endpoint to view the number of currently connected clients.&lt;/p&gt;

&lt;p&gt;Go back to &lt;code&gt;app.py&lt;/code&gt; and add the following code:&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;# app.py
&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/client-count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&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;count&lt;/span&gt;&lt;span class="sh"&gt;"&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;clients&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, if you've ever worked with Flask or Falcon, this code shouldn't come as a surprise. All we do here is send the number of &lt;code&gt;clients&lt;/code&gt; (obtained from the &lt;code&gt;clients&lt;/code&gt; provider) in a JSON response.&lt;/p&gt;

&lt;p&gt;Go ahead! Run &lt;code&gt;python app.py&lt;/code&gt; and run a few &lt;code&gt;python client.py&lt;/code&gt; instances. Check out how many clients are connected by opening &lt;a href="http://localhost:8000/client-count" rel="noopener noreferrer"&gt;http://localhost:8000/client-count&lt;/a&gt; in a web browser. Press &lt;code&gt;Ctrl+C&lt;/code&gt; for one of the clients, and see the client count go down!&lt;/p&gt;

&lt;p&gt;Did it work? Congrats! ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;We're mostly done in terms of the features we wanted to cover together. I've got some ideas you can explore as exercises, of course, but before getting to that let's write some tests.&lt;/p&gt;

&lt;p&gt;One of Bocadillo's design principles is to make it easy to write high-quality applications. As such, Bocadillo has all the tools built-in to write tests for this chatbot server.&lt;/p&gt;

&lt;p&gt;You can write those with your favorite test framework. We'll choose &lt;a href="https://docs.pytest.org" rel="noopener noreferrer"&gt;pytest&lt;/a&gt; for the purpose of this tutorial. Let's install it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--dev&lt;/span&gt; pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's setup our testing environment. We'll write a &lt;a href="https://docs.pytest.org/en/latest/fixture.html" rel="noopener noreferrer"&gt;pytest fixture&lt;/a&gt; that sets up a test client. The test client exposes a Requests-like API as well as helpers to test WebSocket endpoints. Besides, we don't actually need to test the chatbot here, so we'll override the &lt;code&gt;diego&lt;/code&gt; provider with an "echo" mock — this will have the nice side effect of greatly speeding up the tests.&lt;/p&gt;

&lt;p&gt;So, go ahead and create a &lt;code&gt;conftest.py&lt;/code&gt; script, and place the following in there:&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;# conftest.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bocadillo.testing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_client&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="nd"&gt;@provider&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diego&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EchoDiego&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_response&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;query&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;query&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EchoDiego&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;create_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now is the time to write some tests! Create a &lt;code&gt;test_app.py&lt;/code&gt; file at the project root directory:&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="nb"&gt;touch &lt;/span&gt;test_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, let's test that we can connect to the WebSocket endpoint, and that we get a response from Diego if we send a message:&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;# test_app.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_connect_and_converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_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;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&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;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's test the incrementation of the client counter when clients connect to the WebSocket endpoint:&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;# test_app.py
&lt;/span&gt;&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_client_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&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;/client-count&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="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;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_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;/conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&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;/client-count&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="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;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&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;/client-count&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="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;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run these tests using:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And, well, guess what?&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; &lt;span class="nb"&gt;test &lt;/span&gt;session starts &lt;span class="o"&gt;=====================&lt;/span&gt;
platform darwin &lt;span class="nt"&gt;--&lt;/span&gt; Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: ..., inifile: pytest.ini
collected 2 items

test_app.py ..                                         &lt;span class="o"&gt;[&lt;/span&gt;100%]

&lt;span class="o"&gt;==================&lt;/span&gt; 2 passed &lt;span class="k"&gt;in &lt;/span&gt;0.08 seconds &lt;span class="o"&gt;==================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tests pass! ✅🎉&lt;/p&gt;

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

&lt;p&gt;If you've made it so far — congratulations! You've just built a &lt;strong&gt;chatbot server&lt;/strong&gt; powered by WebSocket, &lt;a href="https://github.com/gunthercox/ChatterBot" rel="noopener noreferrer"&gt;ChatterBot&lt;/a&gt; and Bocadillo.&lt;/p&gt;

&lt;p&gt;In this article, we've seen how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup a Bocadillo project.&lt;/li&gt;
&lt;li&gt;Write a WebSocket endpoint.&lt;/li&gt;
&lt;li&gt;Write an HTTP endpoint.&lt;/li&gt;
&lt;li&gt;Use providers to decouple resources and their consumers.&lt;/li&gt;
&lt;li&gt;Test WebSocket and HTTP endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete code for this tutorial is available on the Bocadillo repo on GitHub: &lt;a href="https://github.com/bocadilloproject/bocadillo/blob/release/docs/docs/getting-started/tutorial" rel="noopener noreferrer"&gt;get the code!&lt;/a&gt; All in all, the server and &lt;code&gt;providerconf.py&lt;/code&gt; only add up to about 60 lines of code — pretty good bang for the buck!&lt;/p&gt;

&lt;p&gt;Obviously, we've only scratched the surface of what you can do with Bocadillo. The goal of this tutorial was to take you through the steps of building a &lt;em&gt;Minimum Meaningful Application&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You can iterate upon this chatbot server we've built together very easily. I'd be interested to see what you come up with!&lt;/p&gt;

&lt;p&gt;Want to challenge yourself? Here are a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a home page rendered with &lt;a href="https://bocadilloproject.github.io/guides/agnostic/templates.html" rel="noopener noreferrer"&gt;templates&lt;/a&gt;. The web browser should connect to the chatbot server via a JavaScript program. You'll probably also need to serve &lt;a href="https://bocadilloproject.github.io/guides/http/static-files.html" rel="noopener noreferrer"&gt;static files&lt;/a&gt; to achieve this.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chatterbot.readthedocs.io/en/stable/training.html" rel="noopener noreferrer"&gt;Train Diego&lt;/a&gt; to answers questions like "How many people are you talking to currently?"&lt;/li&gt;
&lt;li&gt;Currently, all clients talk to the same instance of Diego. Yet, it would be nice if each client had their own Diego to ensure a bespoke conversation. You may want to investigate &lt;a href="https://bocadilloproject.github.io/guides/agnostic/sessions.html" rel="noopener noreferrer"&gt;cookie-based sessions&lt;/a&gt; and &lt;a href="https://bocadilloproject.github.io/guides/injection/factory.html" rel="noopener noreferrer"&gt;factory providers&lt;/a&gt; to implement this behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you enjoyed this tutorial! If you'd like to support the project, be sure to &lt;a href="https://github.com/bocadilloproject/bocadillo" rel="noopener noreferrer"&gt;star the repo&lt;/a&gt;. If you don't want to miss on new releases and announcements, feel free to follow &lt;a href="https://twitter.com/bocadillopy" rel="noopener noreferrer"&gt;@bocadillopy&lt;/a&gt; on Twitter!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Consuming APIs in Angular: Displaying Data In Components</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Fri, 01 Mar 2019 10:35:53 +0000</pubDate>
      <link>https://forem.com/florimondmanca/consuming-apis-in-angular-displaying-data-in-components-3b97</link>
      <guid>https://forem.com/florimondmanca/consuming-apis-in-angular-displaying-data-in-components-3b97</guid>
      <description>&lt;p&gt;Welcome back! This article is a follow up to a previous article:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/florimondmanca" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JcCkjaY6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--iNnvH8KG--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/89633/3cb14893-72e9-40ab-8f02-24c7b7df3f17.JPG" alt="florimondmanca"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/florimondmanca/consuming-apis-in-angular--the-model-adapter-pattern-3fk5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Consuming APIs in Angular – The Model-Adapter Pattern&lt;/h2&gt;
      &lt;h3&gt;Florimond Manca ・ Sep 5 '18 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devtips&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;If you haven't read it yet — go check it out! This post will be referencing it quite often.&lt;/p&gt;

&lt;p&gt;I recently had a question from &lt;a class="mentioned-user" href="https://dev.to/milankovach"&gt;@milankovach&lt;/a&gt; about how the &lt;code&gt;CourseService&lt;/code&gt; could be used in a component, say, to display the list of courses.&lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/milankovach"&gt;
        &lt;img class="profile-pic" src="https://res.cloudinary.com/practicaldev/image/fetch/s--kwZmKmIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--UqltHxdX--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_50%2Cq_auto%2Cw_50/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/139182/7b434b8b-9c08-435a-b4e9-abdcd1afa675.png" alt="milankovach profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/milankovach"&gt;
        &lt;span class="comment-username"&gt;Milan Kovac&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2 m:pl-0"&gt;•&lt;/span&gt;

&lt;a href="https://dev.to/milankovach/comment/910k" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time&gt;
    Feb 22 '19
  &lt;/time&gt;

&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;Hi Florimond,&lt;br&gt;
this is great but can you add short description (maybe here in comment section) how to use your services in other components for the n00bs? Thanks! :)&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is exactly what we'll cover in this beginner-friendly article.&lt;/p&gt;

&lt;p&gt;This post should be helpful to anyone wondering &lt;strong&gt;how to retrieve and display data fetched from an external API&lt;/strong&gt;. 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick refresher
&lt;/h2&gt;

&lt;p&gt;In the original post, we discussed a design pattern that I use to standardise how my Angular apps communicate with REST APIs: the &lt;strong&gt;Model-Adapter pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Given the &lt;code&gt;GET /courses&lt;/code&gt; API endpoint, we built a &lt;code&gt;Course&lt;/code&gt; model, a &lt;code&gt;CourseAdapter&lt;/code&gt; and a &lt;code&gt;CourseService&lt;/code&gt; that can help us fetch the list of courses from the API.&lt;/p&gt;

&lt;p&gt;Here's what the project structure looks like for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/app
├── app.component.css
├── app.component.html
├── app.component.ts
├── app.module.ts
└── core
    ├── course.model.ts
    └── course.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal here is to build a &lt;code&gt;CourseListComponent&lt;/code&gt; that fetches the list of courses using the &lt;code&gt;CourseService&lt;/code&gt;, and displays them in the form of a simple unordered list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's build!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Generating the &lt;code&gt;CourseListComponent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Okay, let's get started. First, we will generate the component using the &lt;a href="https://cli.angular.io"&gt;Angular CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate component CourseList
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TypeScript (TS), HTML and CSS files for the component will be generated under  &lt;code&gt;src/app/course-list/&lt;/code&gt;. Here's what the TS file looks like so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// course-list/course-list.component.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-course-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./course-list.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./course-list.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CourseListComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up attributes and templates
&lt;/h3&gt;

&lt;p&gt;As a first step, let's add an empty list of courses on the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  // course-list/course-list.component.ts
  import { Component, OnInit } from '@angular/core';

  @Component({
    selector: 'app-course-list',
    templateUrl: './course-list.component.html',
    styleUrls: ['./course-list.component.css']
  })
  export class CourseListComponent implements OnInit {

+   courses: Courses[];

-   constructor() { }
&lt;span class="gi"&gt;+   constructor() {
+     this.courses = [];
+   }
&lt;/span&gt;
    ngOnInit() {
    }

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

&lt;/div&gt;



&lt;p&gt;Next, let's set up the template. Nothing fancy here, we're just using an &lt;a href="https://angular.io/guide/displaying-data#showing-an-array-property-with-ngfor"&gt;&lt;code&gt;*ngFor&lt;/code&gt;&lt;/a&gt; to display each course in its own list item, as well as the &lt;a href="https://angular.io/api/common/DatePipe"&gt;&lt;code&gt;DatePipe&lt;/code&gt;&lt;/a&gt; to format the date.&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="c"&gt;&amp;lt;!-- course-list/course-list.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let course of courses"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      {{ course.name }} • {{ course.code }} • Created
      {{ course.created | date }}
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we're at it, let's update the &lt;code&gt;AppComponent&lt;/code&gt;'s template to display the list of courses:&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="c"&gt;&amp;lt;!-- app.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Courses&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;app-course-list&amp;gt;&amp;lt;/app-course-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright! Let's fire up the browser, and we should be greeted with the "Courses" title and… an empty list. Why? Well, we haven't fetched any course yet!&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the API endpoint
&lt;/h3&gt;

&lt;p&gt;Before we go and plug the &lt;code&gt;CourseService&lt;/code&gt; in, remember that for now it refers to &lt;code&gt;https://api.myapp.com/courses&lt;/code&gt; — and that API doesn't exist!&lt;/p&gt;

&lt;p&gt;That said, it would be nice to test the &lt;code&gt;CourseService&lt;/code&gt; against a live server, wouldn't it?&lt;/p&gt;

&lt;p&gt;So, let's build a quick backend API for this exact purpose. I'll be using Python and &lt;a href="https://github.com/bocadilloproject/bocadillo"&gt;Bocadillo&lt;/a&gt; (shameless plug here: I am the maintainer of Bocadillo!) to provide the &lt;code&gt;GET /courses&lt;/code&gt; endpoint we need to have access to from the browser.&lt;/p&gt;

&lt;p&gt;You don't need to know about Python nor understand the code below, but I'm displaying it here for those interested:&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;# app.py
# Install: `pip install bocadillo`
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;enable_cors&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="n"&gt;cors_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"allow_origins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"allow_methods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&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;COURSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"adv-maths"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Advanced Mathematics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2018-08-14T12:09:45"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"cs1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Computer Science I"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2018-06-12T18:34:16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/courses"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;courses_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COURSES&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;GET /courses&lt;/code&gt; endpoint will just return a hardcoded list of courses.&lt;/p&gt;

&lt;p&gt;We can fire the API up in a terminal using &lt;code&gt;$ python app.py&lt;/code&gt;, and leave it running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating the API with the &lt;code&gt;CourseService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As a last integration step, we need to update the URL which the &lt;code&gt;CourseService&lt;/code&gt; uses to fetch the courses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core/course.service.ts&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CourseService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000/courses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CourseAdapter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// Adapt each item in the raw data array&lt;/span&gt;
      &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adapt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fetching courses with the &lt;code&gt;CourseService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We're now ready to plug the &lt;code&gt;CourseService&lt;/code&gt; into the &lt;code&gt;CourseListComponent&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Here are the steps we'll take to do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import&lt;/strong&gt; the service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inject&lt;/strong&gt; it in the component using Angular's &lt;a href="https://www.angular.io/guide/dependency-injection"&gt;dependency injection&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the component's &lt;code&gt;ngOnInit()&lt;/code&gt; method, get the &lt;a href="https://angular.io/guide/rx-library"&gt;RxJS&lt;/a&gt; observable to the list of course and &lt;strong&gt;subscribe&lt;/strong&gt; to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store&lt;/strong&gt; the fetched list of course on the component so that it gets rendered in the template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wondering how that translates into code? Take a look below — I added landmarks for each of the steps above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Course&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../core/course.model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// (1) Import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CourseService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../core/course.service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app-course-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./course-list.component.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./course-list.component.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CourseListComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// (2) Inject&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;courseService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CourseService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// (3) Subscribe&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;courseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// (4) Store&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Celebrate
&lt;/h3&gt;

&lt;p&gt;There you go! If we open the browser at &lt;code&gt;http://localhost:8000&lt;/code&gt;, we see the list of courses displayed in sexy Times New Roman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8_UqZGH5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/1e5a9e87-ce68-4825-b972-790fa84d55f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8_UqZGH5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/1e5a9e87-ce68-4825-b972-790fa84d55f5.png" alt="" width="500" height="356"&gt;&lt;/a&gt;&lt;br&gt;
Styling is, indeed, out of the scope of this blog post.&lt;/p&gt;

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

&lt;p&gt;Alright, let's see what we've achieved here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We generated the &lt;code&gt;CourseListComponent&lt;/code&gt; using &lt;a href="https://cli.angular.io"&gt;Angular CLI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We set up the component's &lt;code&gt;courses&lt;/code&gt; attribute and its [template].&lt;/li&gt;
&lt;li&gt;We used Python and &lt;a href="https://bocadilloproject.github.io"&gt;Bocadillo&lt;/a&gt; to build the API endpoint to test our component against.&lt;/li&gt;
&lt;li&gt;We used the &lt;code&gt;CourseService&lt;/code&gt; and &lt;a href="https://angular.io/guide/rx-library"&gt;RxJS&lt;/a&gt; to fetch the list of course.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In fact, this is quite a typical workflow for me when I build web apps using Angular — I start by stubbing out the components, then implement the backend endpoints I need, and integrate them with the services to finally display the data.&lt;/p&gt;

&lt;p&gt;If you're interested in the code, I uploaded it to a GitHub repo: &lt;a href="https://github.com/florimondmanca/ng-courses"&gt;ng-courses&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Built A Python Web Framework And Became An Open Source Maintainer</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Sun, 23 Dec 2018 19:21:42 +0000</pubDate>
      <link>https://forem.com/bocadillo/how-i-built-a-python-web-framework-and-became-an-open-source-maintainer-3okd</link>
      <guid>https://forem.com/bocadillo/how-i-built-a-python-web-framework-and-became-an-open-source-maintainer-3okd</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://blog.florimond.dev/how-i-built-a-web-framework-and-became-an-open-source-maintainer"&gt;blog.florimond.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's been a while since I've written a blog post. Nearly two months, actually. So, where have I been?&lt;/p&gt;

&lt;p&gt;For one, the final year of my engineering studies has been taking up more of my time than I thought it would. I'm not complaining, though — I've been learning a ton of interesting and useful things, both theoretical and practical. Plus, I want to take the opportunity to live this final year fully.&lt;/p&gt;

&lt;p&gt;The other side of the story is — I've been working on building &lt;a href="https://bocadilloproject.github.io"&gt;Bocadillo&lt;/a&gt;, an open source asynchronous Python web framework. The adventure, which started as a way for me to learn about the internals of a web framework, has been thrilling so far.&lt;/p&gt;

&lt;p&gt;This means that, because a day is only 24 hours, I had to put blogging aside for a bit. But now that I'm on holiday, I can finally take the time to reflect on what's been going on. 🥳&lt;/p&gt;

&lt;p&gt;In this blog post, I want to put on paper my thoughts about the whole process of building a web framework and especially &lt;strong&gt;launching an open source project&lt;/strong&gt;. I've already learnt a lot in the process, from programming techniques to project management and development tooling, so I wanted to share my experience with you!&lt;/p&gt;

&lt;p&gt;What's on the menu?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
What is Bocadillo?: introducing Bocadillo in order to set up some context.&lt;/li&gt;
&lt;li&gt;
The story behind it all: I'll tell you the story of how it became my first open source project!&lt;/li&gt;
&lt;li&gt;
Tips to start your own open source project: a series &lt;strong&gt;tips&lt;/strong&gt; I gathered from experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First things first — let me introduce you to Bocadillo! (This will be short and sweet, I promise.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Bocadillo?
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/bocadilloproject"&gt;
        bocadilloproject
      &lt;/a&gt; / &lt;a href="https://github.com/bocadilloproject/bocadillo"&gt;
        bocadillo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      (UNMAINTAINED) Fast, scalable and real-time capable web APIs for everyone
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
NOTICE&lt;/h1&gt;
&lt;p&gt;Bocadillo is now &lt;strong&gt;UNMAINTAINED&lt;/strong&gt;. This repository should be archived soon. We recommend users to migrate to other well-supported alternatives, such as &lt;a href="https://www.starlette.io" rel="nofollow"&gt;Starlette&lt;/a&gt; or &lt;a href="https://fastapi.tiangolo.com" rel="nofollow"&gt;FastAPI&lt;/a&gt;. Please see &lt;a href="https://github.com/bocadilloproject/bocadillo/issues/334"&gt;#334&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;
    &lt;a rel="noopener noreferrer" href="https://github.com/bocadilloproject/bocadillo/blob/master/.github/banner.png?raw=true"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tfGZG_Ly--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/bocadilloproject/bocadillo/raw/master/.github/banner.png%3Fraw%3Dtrue"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://travis-ci.org/bocadilloproject/bocadillo" rel="nofollow"&gt;
        &lt;img src="https://camo.githubusercontent.com/0b2a8c68d1d907db1a168e1d89570ba49949f2a97ff49ca3bd2a62f37f518436/68747470733a2f2f7472617669732d63692e6f72672f626f636164696c6c6f70726f6a6563742f626f636164696c6c6f2e7376673f6272616e63683d6d6173746572" alt="Build status"&gt;
    &lt;/a&gt;
    &lt;a href="https://codecov.io/gh/bocadilloproject/bocadillo" rel="nofollow"&gt;
        &lt;img src="https://camo.githubusercontent.com/5571f90fa49e0274fa6820b7dffcbea0f82a6f4f564682c2df7b4294a9941d21/68747470733a2f2f636f6465636f762e696f2f67682f626f636164696c6c6f70726f6a6563742f626f636164696c6c6f2f6272616e63682f6d61737465722f67726170682f62616467652e737667" alt="Test coverage"&gt;
    &lt;/a&gt;
    &lt;a href="https://pypi.org/project/bocadillo" rel="nofollow"&gt;
        &lt;img src="https://camo.githubusercontent.com/9bfedafd9e80d3ea900791ffcc98a240dba9ac6359cfd3ba57a05cdec2f542b8/68747470733a2f2f62616467652e667572792e696f2f70792f626f636164696c6c6f2e737667" alt="pypi version"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/ambv/black"&gt;
        &lt;img src="https://camo.githubusercontent.com/aaa8def7ee55156a7bce1eb408897b26f9bad3977342abd9e6ad554af48e1ae5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d626c61636b2d3030303030302e737667" alt="code style"&gt;
    &lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;Documentation: &lt;a href="https://bocadilloproject.github.io" rel="nofollow"&gt;https://bocadilloproject.github.io&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Bocadillo is a &lt;strong&gt;Python async web framework&lt;/strong&gt; that makes building performant and highly concurrent web APIs fun and accessible to everyone.&lt;/p&gt;

&lt;h2&gt;
Requirements&lt;/h2&gt;
&lt;p&gt;Python 3.6+&lt;/p&gt;
&lt;h2&gt;
Installation&lt;/h2&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pip install bocadillo&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
Example&lt;/h2&gt;
&lt;div class="highlight highlight-source-python position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;bocadillo&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-v"&gt;App&lt;/span&gt;, &lt;span class="pl-s1"&gt;configure&lt;/span&gt;

&lt;span class="pl-s1"&gt;app&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;App&lt;/span&gt;()
&lt;span class="pl-en"&gt;configure&lt;/span&gt;(&lt;span class="pl-s1"&gt;app&lt;/span&gt;)

&lt;span class="pl-en"&gt;@&lt;span class="pl-s1"&gt;app&lt;/span&gt;.&lt;span class="pl-en"&gt;route&lt;/span&gt;(&lt;span class="pl-s"&gt;"/"&lt;/span&gt;)&lt;/span&gt;
&lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;index&lt;/span&gt;(&lt;span class="pl-s1"&gt;req&lt;/span&gt;, &lt;span class="pl-s1"&gt;res&lt;/span&gt;):
    &lt;span class="pl-s1"&gt;res&lt;/span&gt;.&lt;span class="pl-s1"&gt;json&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; {&lt;span class="pl-s"&gt;"hello"&lt;/span&gt;: &lt;span class="pl-s"&gt;"world"&lt;/span&gt;}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Save this as &lt;code&gt;app.py&lt;/code&gt;, then start a &lt;a href="https://www.uvicorn.org" rel="nofollow"&gt;uvicorn&lt;/a&gt; server (hot reload enabled!):&lt;/p&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;uvicorn app:app --reload&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Say hello!&lt;/p&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ curl http://localhost:8000
{&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;hello&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;world&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Ready to dive in? &lt;a href="https://bocadilloproject.github.io" rel="nofollow"&gt;Visit the documentation site&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Changelog&lt;/h2&gt;
&lt;p&gt;All changes to Bocadillo are recorded in the…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/bocadilloproject/bocadillo"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;
  
  
  Elevator pitch
&lt;/h3&gt;

&lt;p&gt;Bocadillo is a &lt;strong&gt;modern Python web framework&lt;/strong&gt; that provides a sane toolkit for building performant web applications and services using &lt;strong&gt;asynchronous programming&lt;/strong&gt;. It is compatible with Python 3.6+ and MIT-licensed.&lt;/p&gt;

&lt;p&gt;Want to get started? &lt;a href="https://bocadilloproject.github.io"&gt;Read the documentation&lt;/a&gt;!&lt;/p&gt;
&lt;h3&gt;
  
  
  Key figures
&lt;/h3&gt;

&lt;p&gt;Bocadillo's initial commit was on November 3rd, 2018. As of December 21st, i.e. a month and a half later, here's where Bocadillo stands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500+ commits and 31 stars on the &lt;a href="https://github.com/bocadilloproject/bocadillo"&gt;bocadilloproject/bocadillo&lt;/a&gt; repo — feel free to add yours!&lt;/li&gt;
&lt;li&gt;10 releases to &lt;a href="https://pypi.org/project/bocadillo"&gt;PyPI&lt;/a&gt; — latest being v0.7&lt;/li&gt;
&lt;li&gt;8k+ downloads as measured by &lt;a href="https://pepy.tech/project/bocadillo"&gt;PePy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About 30 pages of &lt;a href="https://bocadilloproject.github.io"&gt;docs&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;2 open source contributors — and many others welcome! 🥳&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These figures are of course very modest, but I'm already very happy with them. Of course, if you want to support Bocadillo, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bocadilloproject/bocadillo"&gt;Star the repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bocadilloproject/bocadillo/blob/master/CONTRIBUTING.md"&gt;Become a contributor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/bocadillopy"&gt;Follow Bocadillo on Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, if you've already tried Bocadillo out, please get in touch via Twitter or the &lt;a href="https://gitter.im/bocadilloproject/bocadillo"&gt;Gitter&lt;/a&gt; chat room — I'd LOVE to hear from you!&lt;/p&gt;
&lt;h3&gt;
  
  
  Philosophy
&lt;/h3&gt;

&lt;p&gt;In terms of philosophy, Bocadillo is &lt;strong&gt;beginner-friendly&lt;/strong&gt;, but it aims at giving power-users the flexibility they need. It focuses on &lt;strong&gt;developer experience&lt;/strong&gt; while encouraging best practices.&lt;/p&gt;

&lt;p&gt;Besides, Bocadillo is not meant to be minimalist (but not a mastodon either). The idea is to include &lt;strong&gt;a carefully chosen set of included batteries&lt;/strong&gt;, with sensible defaults, so that you can &lt;strong&gt;solve common problems&lt;/strong&gt; and &lt;strong&gt;be productive right away&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Goals
&lt;/h3&gt;

&lt;p&gt;My goal is that people embrace the &lt;strong&gt;new possibilities of async Python&lt;/strong&gt; and that Bocadillo becomes &lt;strong&gt;a tool that helps people solve real-world problems more easily and efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There's still a lot of work ahead to get there, but the marathon has started!&lt;/p&gt;

&lt;p&gt;Speaking of &lt;em&gt;async&lt;/em&gt; Python, let me address a question you may have already asked yourself…&lt;/p&gt;
&lt;h3&gt;
  
  
  What with async?
&lt;/h3&gt;

&lt;p&gt;Generally, a web app instance spends a lot (if not most) of its request processing time waiting for I/O to complete — API calls, database queries, filesystem operations. etc. Most of these operations are &lt;em&gt;blocking&lt;/em&gt;, which typically limits performance if multiple clients are requesting the server.&lt;/p&gt;

&lt;p&gt;The idea with async frameworks like Bocadillo is to build &lt;strong&gt;apps that do not block on I/O operations&lt;/strong&gt;. To achieve this, we leverage &lt;strong&gt;asynchronous programming&lt;/strong&gt; and recent additions to the Python language such as &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio&lt;/a&gt; and &lt;a href="https://www.python.org/dev/peps/pep-0492/"&gt;async/await&lt;/a&gt; — available from Python 3.4+ and 3.6+ respectively. This allows us to consider processing a request as a task which is "scheduled" to run in a near future, i.e. when the CPU is avaiable.&lt;/p&gt;

&lt;p&gt;As a result, beyond making better use of the CPU, this architecture has a very interesting advantage — we can now &lt;strong&gt;handle multiple requests &lt;em&gt;concurrently&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;(Note: I didn't write &lt;em&gt;in parallel&lt;/em&gt;, as async still uses a single thread. &lt;a href="https://www.youtube.com/watch?v=cN_DpYBzKso"&gt;Concurrency is not parallelism&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;This property ultimately results in &lt;strong&gt;more stable throughput and performance&lt;/strong&gt; as the number of concurrent clients increases. From my own (yet to be published) benchmarks, Bocadillo keeps a steady processing rate whether it talks to 10 or 10,000 clients. On the other hand, "sync" frameworks like Flask or Django show a significant drop in reqs/sec in high concurrency settings.&lt;/p&gt;

&lt;p&gt;Eager for more on Python asynchronous programming? Here are a few talks I recommend, perhaps to be watched in this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=iG6fr81xHKA"&gt;Asynchronous Python for the Complete Beginner&lt;/a&gt;, Miguel Grinberg, Pycon 2017.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=m28fiN9y_r8&amp;amp;t=132s"&gt;Async/await in Python 3.5 and why it is awesome&lt;/a&gt;, Yuri Selivanov, EuroPython 2016.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=E-1Y4kSsAFc"&gt;Fear and Awaiting in Async: A Savage Journey to the Heart of the Coroutine Dream&lt;/a&gt;, David Beazley, PyOhio 2016.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Bocadillo is built on &lt;a href="https://www.uvicorn.org"&gt;Uvicorn&lt;/a&gt;, the lightning-fast ASGI web server, and &lt;a href="https://www.starlette.io"&gt;Starlette&lt;/a&gt;, a handy ASGI toolkit. Both were created by &lt;a href="https://github.com/tomchristie"&gt;Tom Christie&lt;/a&gt;, a core contributor to the Django REST Framework (among other things).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;a href="https://asgi.readthedocs.io"&gt;ASGI&lt;/a&gt; is the asynchronous equivalent to WSGI, i.e. a specification for how web servers should communicate with &lt;em&gt;asynchronous&lt;/em&gt; Python web applications.&lt;/p&gt;
&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What Bocadillo already has&lt;/strong&gt;: requests, responses, views (function-based and classed-based), routes and route parameters, media types, redirections, templates, static files, background tasks, CORS, HSTS, GZip, "recipes" (a.k.a. blueprints), middleware, hooks, and even a CLI. There are more in the release pipeline!&lt;/p&gt;

&lt;p&gt;One thing that Bocadillo does &lt;em&gt;not&lt;/em&gt; have (yet), though, is a &lt;strong&gt;database layer&lt;/strong&gt;. Most web apps or APIs I've built needed to persist data in some way, so I believe this (or at least an official recommendation for how to integrate an async database layer such as &lt;a href="https://tortoise-orm.readthedocs.io/en/latest/"&gt;Tortoise ORM&lt;/a&gt;) should land into the framework at some point.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hello, world!
&lt;/h3&gt;

&lt;p&gt;Let's finish with the traditional "Hello, World" script!&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;# api.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bocadillo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;res&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="s"&gt;"Hello, World!"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The story behind it all
&lt;/h2&gt;

&lt;p&gt;Alright, enough of pitching Bocadillo! Now that you know what it is, I want to share with you the story that led me to write this very blog post.&lt;/p&gt;

&lt;p&gt;How and why did it begin? What were some of the most meaningful events? Let's figure this out.&lt;/p&gt;

&lt;h3&gt;
  
  
  A learn-by-doing project
&lt;/h3&gt;

&lt;p&gt;Bocadillo started as &lt;strong&gt;a way for me to learn more about the internals of a web framework&lt;/strong&gt;. I wanted to get behind the scenes after nearly 2 years of using various Python and JS web frameworks. I wanted to know how it &lt;em&gt;actually&lt;/em&gt; all worked.&lt;/p&gt;

&lt;p&gt;To be clear, Bocadillo didn't start with a very detailed plan. Heck, I didn't even think about whether there was a need for an(other) Python async web framework. I wanted to &lt;strong&gt;learn&lt;/strong&gt; more than anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's reinvent the wheel, and release it ASAP
&lt;/h3&gt;

&lt;p&gt;So there I was, on the 3rd of November, implementing features so common it felt like &lt;strong&gt;reinventing the wheel&lt;/strong&gt;. These were features like requests, responses, views, routes or the application server. "Hundreds of web frameworks out there already solved these problems before", I thought…&lt;/p&gt;

&lt;p&gt;But I didn't really care. As &lt;a class="mentioned-user" href="https://dev.to/funkybob"&gt;@funkybob&lt;/a&gt; kindly &lt;a href="https://twitter.com/BunkyFob/status/1059960013689516032"&gt;twitted to me&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reinventing the wheel is an awesome way to learn… and sometimes what you learn is just how much your existing frameworks are doing for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A solid argument in favour of reinventing the wheel which made me realize how much Django is an absolute massive beast.&lt;/p&gt;

&lt;p&gt;Anyway, this initial endeavour led me to release &lt;a href="https://pypi.org/project/bocadillo/0.1.0/"&gt;v0.1&lt;/a&gt; on PyPI on the 4th of November. &lt;strong&gt;Just two days after the initial commit&lt;/strong&gt;, people could already &lt;code&gt;pip install bocadillo&lt;/code&gt; and build a minimal async web app. (Who said Python packaging was a pain? 🐍)&lt;/p&gt;

&lt;h3&gt;
  
  
  First signs of potential
&lt;/h3&gt;

&lt;p&gt;After v0.1 was released, I carried on with implementing more features such as new types of responses or error handling.&lt;/p&gt;

&lt;p&gt;On November 6th, v0.2.1 was out. That's when I began to realize that Bocadillo was a good candidate for my first &lt;strong&gt;full-blown open source project&lt;/strong&gt;. The idea seemed appealing to me, so I went for it!&lt;/p&gt;

&lt;p&gt;At that point, I hadn't disclosed anything about Bocadillo yet, not even to friends, so I wanted to make a first announcement. I chose to do it on Twitter.&lt;/p&gt;

&lt;p&gt;Because Bocadillo's initial code design and implementation took heavy inspiration from &lt;a href="https://python-responder.org"&gt;Responder&lt;/a&gt;, Kenneth Reitz's own async framework, &lt;a href="https://twitter.com/kennethreitz/status/1059942147342942209"&gt;I decided to give a shoutout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O8UCeKaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/22e7446d-09a0-4508-b5a1-3c2c5a629a53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O8UCeKaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/22e7446d-09a0-4508-b5a1-3c2c5a629a53.png" alt="The first announcement about Bocadillo on Twitter, and Kenneth Reitz's answer. ❣️" width="492" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
The first announcement about Bocadillo on Twitter, and Kenneth Reitz's answer. ❣️



&lt;p&gt;Kenneth's answer and the forthcoming reactions after he retweeted the announcement made me think that &lt;strong&gt;Bocadillo actually had potential&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In just a day, the repo got 20 stars (a personal record already!) and, although it may look trivial, I thought it was really cool.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Psst: if you want to help get Bocadillo known, you can &lt;a href="https://github.com/bocadilloproject/bocadillo"&gt;star it&lt;/a&gt; too and spread the news!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, after v0.2 was released, I felt motivated to keep working on Bocadillo, and add more features.&lt;/p&gt;

&lt;p&gt;Up until I realized something…&lt;/p&gt;

&lt;h3&gt;
  
  
  Where are the docs? Like, &lt;em&gt;real&lt;/em&gt; docs?
&lt;/h3&gt;

&lt;p&gt;It was clear for me: I wanted Bocadillo to be my first &lt;strong&gt;open source project&lt;/strong&gt;. I wanted to take it &lt;em&gt;seriously&lt;/em&gt; in order to learn as much as I can from the process.&lt;/p&gt;

&lt;p&gt;So, right from the start, I wrote an informative &lt;em&gt;README&lt;/em&gt;, curated a &lt;em&gt;CHANGELOG&lt;/em&gt; (with the help of &lt;a href="https://keepachangelog.com"&gt;keep a changelog&lt;/a&gt;) and added &lt;em&gt;CONTRIBUTING&lt;/em&gt; guidelines. As more and more releases went out between November 6th and November 18th, I updated the changelog and documented new features in the repo's README.&lt;/p&gt;

&lt;p&gt;Quickly though, this became impractical. The README was growing in size and it became hard to navigate, even with a table of contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AV9IUxSq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/f8afa87e-0fd0-432c-b11b-1ef77a12e2bb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AV9IUxSq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/f8afa87e-0fd0-432c-b11b-1ef77a12e2bb.png" alt='The table of contents for the repo on November 17th. See that "Usage" section growing to an astronomical size?' width="237" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
The table of contents for the repo on November 17th. See that "Usage" section growing to an astronomical size?



&lt;p&gt;That's when I realized &lt;strong&gt;I needed proper documentation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you think about it, &lt;strong&gt;good documentation is a &lt;em&gt;sine qua non&lt;/em&gt; condition to having people use what you've built&lt;/strong&gt;. And that lengthy README was not good documentation considering the size that Bocadillo was heading at.&lt;/p&gt;

&lt;p&gt;Then, it hit me — a lot of large-scale open source tools, libraries or frameworks I use and love have a &lt;strong&gt;documentation site&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is what led me to release v0.5 on November 18th with a major addition: a brand new &lt;a href="https://bocadilloproject.github.io"&gt;docs site&lt;/a&gt;, which I built with &lt;a href="https://vuepress.vuejs.org"&gt;VuePress&lt;/a&gt; and hosted on &lt;a href="https://pages.github.com"&gt;GitHub Pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rND3xJPK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/140491ee-272e-43a7-86a9-c18721022f9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rND3xJPK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/140491ee-272e-43a7-86a9-c18721022f9f.png" alt="Bocadillo's documentation site home page (end of November, 2018)." width="500" height="247"&gt;&lt;/a&gt;&lt;/p&gt;
Bocadillo's documentation site home page (end of November, 2018).



&lt;p&gt;On the necessity of good documentation — Joe Mancuso, creator of the &lt;a href="https://masoniteframework.gitbooks.io"&gt;Masonite&lt;/a&gt; framework, once shared with me this great piece of advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it's not documented, it doesn't exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's why I'm taking documentation very seriously and strive to make it as good as I can — and so should you with &lt;em&gt;every&lt;/em&gt; project you're working on.&lt;/p&gt;

&lt;p&gt;Then, while building the docs site, I made what I now consider as a very important move for &lt;em&gt;any&lt;/em&gt; open source project…&lt;/p&gt;

&lt;h3&gt;
  
  
  Letting Bocadillo stand on its own feet
&lt;/h3&gt;

&lt;p&gt;Before releasing the docs, I moved Bocadillo from a personal repo to its own GitHub organization, namely &lt;a href="https://github.com/bocadilloproject"&gt;BocadilloProject&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main motivation at the time was that I could use the organization's GitHub Pages domain &lt;code&gt;bocadilloproject.github.io&lt;/code&gt; for the documentation. It is definitely way cleaner and more accessible than &lt;code&gt;florimondmanca.github.io/bocadillo&lt;/code&gt;. 🙃&lt;/p&gt;

&lt;p&gt;However, this had the positive effect of giving Bocadillo &lt;strong&gt;an online space of its own&lt;/strong&gt;. It was not tied to my personal GitHub account anymore — the organisation was the new home for Bocadillo's source code.&lt;/p&gt;

&lt;p&gt;Later, as I realized that Bocadillo announcements was taking over my personal Twitter account, I created a dedicated Twitter account.&lt;/p&gt;

&lt;p&gt;The point is: &lt;strong&gt;it's important that an open source project lives outside of its creators&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, back to the story — on November 18th, I had a docs site up and running that people could visit. What next?&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening up the development process
&lt;/h3&gt;

&lt;p&gt;Up to November 20th, the way I kept track of the backlog and progress was via a private Trello board.&lt;/p&gt;

&lt;p&gt;This was very practical to me: I use Trello for a variety of things. But I realized that &lt;strong&gt;people visiting the repo had no visibility&lt;/strong&gt; on what was coming next or possible ways they could contribute.&lt;/p&gt;

&lt;p&gt;In fact, from a visitor's perspective, I think the repo looked like just any other personal project — no issues, no PRs, just a ton of commits from a single person — and &lt;em&gt;not&lt;/em&gt; a community-driven effort, i.e. what I'd like Bocadillo to become.&lt;/p&gt;

&lt;p&gt;So, per the advice of a close friend of mine and inspired by &lt;a href="https://medium.freecodecamp.org/how-i-went-from-being-a-contributor-to-an-open-source-project-maintainer-acd8a6b316f5"&gt;this article by Dhanraj Acharya&lt;/a&gt;, I decided to &lt;strong&gt;open up the development process&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I converted all my Trello cards to GitHub issues, and added meaningful labels (see &lt;a href="https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63"&gt;Sane GitHub labels&lt;/a&gt; by Dave Lunny) and description to them. I think the repo now provides better visibility on the project and feels more encouraging for newcomers.&lt;/p&gt;

&lt;p&gt;I've learnt from this that &lt;strong&gt;open source is not only about opening the source code&lt;/strong&gt;. You've got to be open about the development process, too.&lt;/p&gt;

&lt;p&gt;I now hoped that, with the repo filled with issues and public PRs, it would attract its first open source contributors.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spoiler alert: it did!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Yay! First contributors!
&lt;/h3&gt;

&lt;p&gt;About at the same time, as Bocadillo grew in size, I began to feel the need for external advice. I was afraid that I might be taking bad design decisions or that the code could have been better. In short, &lt;strong&gt;I needed contributors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, still on November 20th, I had the great pleasure of welcoming the first contribution to the repo, in the form of Alin Panaitiu commenting on &lt;a href="https://github.com/bocadilloproject/bocadillo/pull/3"&gt;PR #3&lt;/a&gt; for the new "hooks" feature.&lt;/p&gt;

&lt;p&gt;Alin helped me fix a few things about the feature which I was initially unsure about myself, and suggested ways in which it could be made even more useful. He went as far as forking the repo and sending me a diff showing off a fix.&lt;/p&gt;

&lt;p&gt;Later, on November 23rd, Alin got his first PR merged. Bocadillo had officially gained its first contributor! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vQOlSCOg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/e338cd3d-ca39-4e06-b176-d5ab87a2fa22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vQOlSCOg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://florimondmanca-personal-website.s3.amazonaws.com/media/markdownx/e338cd3d-ca39-4e06-b176-d5ab87a2fa22.png" alt="Screenshot of PR #18." width="500" height="257"&gt;&lt;/a&gt;&lt;/p&gt;
Screenshot of PR #18.



&lt;p&gt;As emotional as I can be, I was moved.&lt;/p&gt;

&lt;p&gt;Even more so that Alin actually sticked around. In the v0.7 release, Alin contributed 2 new features (GZip and ASGI middleware), with a code-to-merged time of probably less than a few hours. Thanks Alin, great stuff!&lt;/p&gt;

&lt;h3&gt;
  
  
  Entering maintenance mode
&lt;/h3&gt;

&lt;p&gt;From the end of November and onwards, the pace of the project slowed down a bit. On one hand, I was in a bit of a rush with school projects and exams being on the agenda, but that didn't explain everything. I was experiencing something else.&lt;/p&gt;

&lt;p&gt;You see, in the beginning, committing code to Bocadillo felt very easy. There was barely any legacy so it was exciting and I had tons of ideas. &lt;strong&gt;Everything remained to be done.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But as more features were added in, it started feeling heavier. Releases took longer to get out — now taking a week instead of days. Testing, refactoring and documentation became major parts of the development process. Plus, I now managed Bocadillo's online presence too.&lt;/p&gt;

&lt;p&gt;Don't get me wrong — I'm not complaining. In fact, I like that I got to go past the initial excitation and entered &lt;strong&gt;maintenance mode&lt;/strong&gt;. Besides, it's definitely a normal shift for a project that's trying to gain momentum and reach out to the community.&lt;/p&gt;

&lt;p&gt;I am now enjoying that working on Bocadillo doesn't have to take me nights like it used to in the beginning. Which leads me to the next point…&lt;/p&gt;

&lt;h3&gt;
  
  
  It's not a sprint, it's a marathon
&lt;/h3&gt;

&lt;p&gt;I recently noticed that my attitude towards the project has changed.&lt;/p&gt;

&lt;p&gt;Instead of rushing in to get features out as quickly as possible, and hoping that a burst of users would pop by, send stars en masse, love the framework and beg for more, I now felt more in peace with the idea that the project growth would be slow.&lt;/p&gt;

&lt;p&gt;This stems from the fact that &lt;strong&gt;maintaining an open source project is a marathon, not a sprint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Put differently, &lt;strong&gt;success should always be a by-product, not a goal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You'll have noticed that Bocadillo's goal statement doesn't mention fame, nor a threshold number of users. It only states that I hope Bocadillo can help &lt;em&gt;some&lt;/em&gt; people &lt;strong&gt;solve problems&lt;/strong&gt;. If that is the case for at least one person, I'll consider it a win. If it becomes the case for a lot of people, I shall only consider it as a side effect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EF6-_9jk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1455849318743-b2233052fcff%3Fixlib%3Drb-1.2.1%26auto%3Dformat%26fit%3Dcrop%26w%3D1350%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EF6-_9jk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1455849318743-b2233052fcff%3Fixlib%3Drb-1.2.1%26auto%3Dformat%26fit%3Dcrop%26w%3D1350%26q%3D80" alt="Two person standing on gray tile paving. @goian, unsplash.com" width="880" height="587"&gt;&lt;/a&gt;&lt;/p&gt;
Two person standing on gray tile paving. @goian, unsplash.com



&lt;p&gt;This is it for the story behind building Bocadillo! As you may have noted from these discussions, I have been loving the experience so far, and I'm actually now convinced that I made the right decision when I decided to go past the fear of judgement and build my own web framework. Which leads me to the last section of this blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips to start your own open source project
&lt;/h2&gt;

&lt;p&gt;If there is one thing I want you to take away from reading this article, it's got to be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Open source is an awesome way to learn. You should start your own project today!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Sounds great", you think, "but how should I proceed? Do you have any advice?"&lt;/p&gt;

&lt;p&gt;Well, I do have some. 😋&lt;/p&gt;

&lt;p&gt;First and foremost, &lt;strong&gt;learn&lt;/strong&gt; as much as you can and make sure to &lt;strong&gt;enjoy yourself&lt;/strong&gt;. Open source should never become a burden, and if it does, try to find ways in which you can start delegating or relying on the community to drive the project forward.&lt;/p&gt;

&lt;p&gt;For the rest, brace yourselves — categorized bullet points ahead!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: if you're unsure about how to implement any of the following items and/or want to see how I used or configured the tools in practice, feel free to check out &lt;a href="https://github.com/bocadilloproject"&gt;Bocadillo's repo&lt;/a&gt; and just copy the bits you're interested in — it's open source, after all!&lt;/p&gt;

&lt;h3&gt;
  
  
  Project definition
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Decide &lt;strong&gt;what&lt;/strong&gt; you want to build.&lt;/li&gt;
&lt;li&gt;Decide &lt;strong&gt;why&lt;/strong&gt; you want to build it — although it doesn't have to be deep or abstract, having a clear source of motivation helps.&lt;/li&gt;
&lt;li&gt;Think about &lt;strong&gt;scope&lt;/strong&gt; and &lt;strong&gt;design philosophy&lt;/strong&gt;: this will help make informed design decisions, and prevent feature creep.&lt;/li&gt;
&lt;li&gt;Decide &lt;strong&gt;who&lt;/strong&gt; you are targetting: are your users web developers, sys admins, project managers…?&lt;/li&gt;
&lt;li&gt;Explicitly define &lt;strong&gt;user skills expectations&lt;/strong&gt;: what should your users be familiar with, and how much?&lt;/li&gt;
&lt;li&gt;Decide &lt;strong&gt;how&lt;/strong&gt; you will distribute your project (e.g. a PyPI package).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Marketing &amp;amp; Communication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Build an &lt;strong&gt;identity&lt;/strong&gt;: a name and a tagline at least. Make them short, catchy, and memorable. Showcase them everywhere (repo, PyPI page, docs site…).&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;visual identity&lt;/strong&gt;: this is your logo and graphic charter. It's better to have an okay temporary logo than no logo at all.&lt;/li&gt;
&lt;li&gt;Decide on an &lt;strong&gt;entry point&lt;/strong&gt;, i.e. a natural online location where people can look up your project. It can be the GitHub repo, a docs site, or whatever seems fit, but it should exist. (For Bocadillo, I believe this is the docs site.)&lt;/li&gt;
&lt;li&gt;Give your project &lt;strong&gt;a life of its own&lt;/strong&gt;: it's a good idea to create a separate GitHub organisation or social media account.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;social media&lt;/strong&gt; to communicate news, annoucements and tips, and start gathering people around the project (I use Twitter for this).&lt;/li&gt;
&lt;li&gt;Provide ways for people to see &lt;strong&gt;where you're heading at&lt;/strong&gt; — a roadmap, a list of issues, an "unreleased" section in the CHANGELOG, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Community
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implement &lt;strong&gt;open source best practices&lt;/strong&gt;: a proper &lt;em&gt;README&lt;/em&gt;, contributing guidelines, a code of conduct, issue/PR templates, etc (use GitHub's checklist!). This will make the repo more welcoming to potential contributors, and show that you care about the community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be supportive and kind&lt;/strong&gt; to others. Thank them for their questions. Provide helpful resources.&lt;/li&gt;
&lt;li&gt;It might be a good idea to create a place for informal discussions. I recently decided to experiment with a &lt;a href="https://gitter.im/bocadilloproject/bocadillo"&gt;Gitter&lt;/a&gt; chat room.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;GitHub issues&lt;/strong&gt; to list your TODOs. That way, when wondering what you should work on next, just pick up a ticket and work on it!&lt;/li&gt;
&lt;li&gt;Set up meaningful issue labels (see &lt;a href="https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63"&gt;Sane GitHub Labels&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;You can also set up a &lt;strong&gt;GitHub project&lt;/strong&gt; to display your issues and PRs in a kanban board.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code quality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up a &lt;strong&gt;CI/CD pipeline&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Be unforgiving on &lt;strong&gt;tests&lt;/strong&gt; — besides ensuring your software works as it is intended to, they'll help you and everyone catch regressions and be confident when making changes (I use &lt;a href="https://pytest.org"&gt;pytest&lt;/a&gt; as a testing framework).&lt;/li&gt;
&lt;li&gt;Measure &lt;strong&gt;test coverage&lt;/strong&gt; (I use &lt;a href="https://pypi.org/project/pytest-cov/"&gt;pytest-cov&lt;/a&gt; for pytest/coverage.py integration, and &lt;a href="https://codecov.io"&gt;CodeCov&lt;/a&gt; for coverage reports).&lt;/li&gt;
&lt;li&gt;Enforce that &lt;strong&gt;PRs pass tests&lt;/strong&gt; before merging.&lt;/li&gt;
&lt;li&gt;Every PR should contain all 3 items: code, tests and docs.&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;code formatter&lt;/strong&gt; to reduce syntax/code style noise in code reviews (I use the opinionated &lt;a href="https://github.com/ambv/black"&gt;Black&lt;/a&gt; formatter along with a &lt;a href="https://pre-commit.com"&gt;pre-commit&lt;/a&gt; hook).&lt;/li&gt;
&lt;li&gt;If you don't have other reviewers, &lt;strong&gt;make PRs to yourself&lt;/strong&gt;, let them settle and come back later. It's easier to see if the code is a mess after a few days.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Write a clear and informed &lt;strong&gt;README&lt;/strong&gt; with at least a project description, install instructions, a quick start example and a link to the docs or somewhere users can learn more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://keepachangelog.com"&gt;Keep a changelog&lt;/a&gt;&lt;/strong&gt;, you'll thank yourself later.&lt;/li&gt;
&lt;li&gt;Build a &lt;strong&gt;docs site&lt;/strong&gt; if you're building more than a simple library (I use &lt;a href="https://vuepress.vuejs.org"&gt;VuePress&lt;/a&gt; as a static site generator).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure your docs&lt;/strong&gt;: tutorials, discussions, how-to's, reference (tip: I use &lt;a href="https://github.com/NiklasRosenstein/pydoc-markdown"&gt;PydocMd&lt;/a&gt; to generate Markdown API reference straight from my Python docstrings).&lt;/li&gt;
&lt;li&gt;Remember: &lt;strong&gt;if it's not documented, it doesn't exist&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add pretty &lt;strong&gt;badges&lt;/strong&gt; to your README (e.g. with &lt;a href="https://shields.io"&gt;shields.io&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Versioning and releasing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;semantic versioning&lt;/strong&gt; (see &lt;a href="https://semver.org"&gt;SemVer&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Automate &lt;strong&gt;version bumping&lt;/strong&gt; with tools such as &lt;a href="https://pypi.org/project/bumpversion/"&gt;bumpversion&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Automate the &lt;strong&gt;release pipeline&lt;/strong&gt;. I use &lt;a href="https://travis-ci.org"&gt;TravisCI&lt;/a&gt; to release tagged commits to PyPI.&lt;/li&gt;
&lt;li&gt;Set up &lt;strong&gt;special release branches&lt;/strong&gt;. For example, I use &lt;code&gt;release/docs&lt;/code&gt; for docs deployment and &lt;code&gt;release/test&lt;/code&gt; for releasing to Test PyPI.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Achievement unlocked?
&lt;/h2&gt;

&lt;p&gt;This blog post is now coming to an end, so let's wrap up!&lt;/p&gt;

&lt;p&gt;Although Bocadillo started as a way for me to learn more about the internals of a web framework, &lt;strong&gt;it has turned into a full-blown open source project&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With all the effort that already went into building the framework, documenting it, configuring the repo and managing releases, &lt;strong&gt;I now start to think of myself as an open source maintainer&lt;/strong&gt; — which I think is a very enriching experience!&lt;/p&gt;

&lt;p&gt;Even though I do take pride for what I have achieved so far, &lt;strong&gt;all of this is also very humbling&lt;/strong&gt;. I now realize how challenging managing an open source project can be, let alone building a community around it. &lt;strong&gt;It's tough!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That said, I do believe that you should go for it and &lt;strong&gt;start your own open source project&lt;/strong&gt;. It could be a simple tool or library, or an entire application framework — either way, you'll &lt;strong&gt;learn a lot&lt;/strong&gt; in the process.&lt;/p&gt;

&lt;p&gt;Of course, feel free to check out the &lt;a href="https://github.com/bocadilloproject"&gt;Bocadillo repo&lt;/a&gt; if you seek ideas on structuring an open source project or setting up tooling. There are also tons of great resources on &lt;a href="https://opensource.guide"&gt;opensource.guide&lt;/a&gt;, so check this website out, too!&lt;/p&gt;

&lt;p&gt;Thanks for reading through this article! As always, feedback is much appreciated. In particular, I'd love to hear &lt;strong&gt;your own stories on maintaining open source projects&lt;/strong&gt;. Plus, if by any chance this article has inspired you, be sure to drop a comment! ❣️&lt;/p&gt;

&lt;p&gt;Best wishes for this holiday season to you all. ✌️&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From Angular To Vue: Feeling Like A Beginner Again</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Thu, 25 Oct 2018 13:44:05 +0000</pubDate>
      <link>https://forem.com/florimondmanca/from-angular-to-vue-feeling-like-a-beginner-again-1d6h</link>
      <guid>https://forem.com/florimondmanca/from-angular-to-vue-feeling-like-a-beginner-again-1d6h</guid>
      <description>&lt;p&gt;&lt;em&gt;Update (30 Oct, 2018): a few days after writing this post, I've already built a good understanding of Vue and its ecosystem. Thanks to everyone who shared useful resources about Vue; it helped me a ton!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've worked on a couple of web projects in the past year or so. I now feel at ease and productive with a few select technologies. In the realm of frontend development, that means &lt;a href="https://angular.io" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;. And to be honest, it feels &lt;em&gt;great&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Yet, I find that I've begun resting on my laurels, and boredom even began to take the upper hand. As a result, I started learning &lt;a href="https://vuejs.org" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this short post, I'll expose my state of mind, the difficulties I am currently encountering and what I do to cope with them in the process of &lt;strong&gt;learning Vue after a year of working with Angular&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did we get there?
&lt;/h2&gt;

&lt;p&gt;I've worked with Angular quite extensively lately. Angular is the first modern web framework I've learnt, and I've built at least three or four different apps of various sizes. I also learnt a ton about modern concepts such as Progressive Web Apps and Server Side Rendering along the way.&lt;/p&gt;

&lt;p&gt;But there's a catch. Every time I am given the opportunity to choose a frontend framework for a new project, I go for Angular. Why? Simply put, &lt;strong&gt;because it's the only one I know and I feel comfortable with&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yet, immediately after taking the decision, a little voice sneaks into my head, and it whispers: "Angular, again? Meh…".&lt;/p&gt;

&lt;p&gt;Don't get me wrong — it does feel &lt;strong&gt;rewarding&lt;/strong&gt; to have become knowledgeable enough at a technology that I can build a whole system without barely looking at the documentation. I feel &lt;strong&gt;proud&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There's also no need &lt;em&gt;per se&lt;/em&gt; to be knowledgeable at every frontend framework out there — there are way too many of them, plus specialising also has its advantages.&lt;/p&gt;

&lt;p&gt;But for me, there's a problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where is the challenge? Where is the difficulty? What is there left to learn?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You see, I do love learning new things. I don't particularly enjoy feeling like a beginner — that's rather uncomfortable — but I always dig the outcome: &lt;strong&gt;I can do things I couldn't do before&lt;/strong&gt;, and it's thrilling.&lt;/p&gt;

&lt;p&gt;That's what motivated me to try something else. Also, some circumstances have helped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take your chance
&lt;/h2&gt;

&lt;p&gt;I boarded on a major school project a few weeks ago. We had the possibility to choose between Angular and Vue.&lt;/p&gt;

&lt;p&gt;I didn't know much about Vue — only a few things I've read here and there, and perhaps a toy project from a year ago. Going for Angular would have been so much easier because I've built up a lot of experience and know-how.&lt;/p&gt;

&lt;p&gt;But guess what? We went for Vue.&lt;/p&gt;

&lt;p&gt;Sometimes, leaving your comfort zone is as simple as asking: "why not?".&lt;/p&gt;

&lt;h2&gt;
  
  
  It won't be easy
&lt;/h2&gt;

&lt;p&gt;I dived in just today, and let me tell you — I'm excited, but &lt;strong&gt;it feels &lt;em&gt;very&lt;/em&gt; uncomfortable&lt;/strong&gt; right now.&lt;/p&gt;

&lt;p&gt;Actually, I feel &lt;strong&gt;frustrated&lt;/strong&gt;. I have to &lt;strong&gt;re-learn&lt;/strong&gt; everything. Why does it all look so similar, yet so different? I felt like I was a master, but I am now a total n00b. Duh.&lt;/p&gt;

&lt;p&gt;So, in this new and unknown ecosystem, I need a &lt;strong&gt;survival strategy&lt;/strong&gt; not to drown under the seemingly huge amount of new things to learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build on what you know
&lt;/h2&gt;

&lt;p&gt;Luckily, I now have a better understanding of the world of frontend development than I had when I first tried Vue. A lot of stuff is already out of my way, and I can focus on the framework and its ecosystem. (Also, Vue has matured a lot, which is very good news.)&lt;/p&gt;

&lt;p&gt;Quite naturally, &lt;strong&gt;I am looking for what is familiar&lt;/strong&gt;. I have already noticed that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both Angular and Vue are component-driven — great!&lt;/li&gt;
&lt;li&gt;Both have a &lt;a href="https://cli.vuejs.org" rel="noopener noreferrer"&gt;CLI&lt;/a&gt; — fantastic! (but I miss &lt;code&gt;ng generate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Both have a templating system based on directives — sweet!&lt;/li&gt;
&lt;li&gt;Both have bidirectional data binding through inputs (&lt;code&gt;@Input()&lt;/code&gt; vs &lt;code&gt;@Prop()&lt;/code&gt;) and outputs/events (&lt;code&gt;@Output()&lt;/code&gt; vs. &lt;code&gt;$emit()&lt;/code&gt;) — nice!&lt;/li&gt;
&lt;li&gt;The template syntax is quite similar: &lt;code&gt;*ngFor&lt;/code&gt; becomes &lt;code&gt;v-for&lt;/code&gt;, &lt;code&gt;[foo]="bar"&lt;/code&gt; becomes &lt;code&gt;:foo="bar"&lt;/code&gt;, etc — stellar!&lt;/li&gt;
&lt;li&gt;Both have extensive and precise &lt;a href="https://vuejs.org/v2/guide/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, and a vibrant community — brilliant!&lt;/li&gt;
&lt;li&gt;I can also use &lt;a href="https://vuejs.org/v2/guide/typescript.html" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; with Vue — yay! 🎉&lt;/li&gt;
&lt;li&gt;I've been digging Angular Material, and there seems to be even more Vue component frameworks out there, like &lt;a href="https://vuetifyjs.com" rel="noopener noreferrer"&gt;Vuetify&lt;/a&gt; — amazing!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At least, this is not a cold start. There are indeed a lot of aspects I can relate to and compare. Phew!&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%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2F7af66c43-a947-4a81-aad8-5d5b6bb55cbc.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%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2F7af66c43-a947-4a81-aad8-5d5b6bb55cbc.png"&gt;&lt;/a&gt;&lt;/p&gt;
Me generating a project using Vue CLI: "TypeScript! There you are!"



&lt;h2&gt;
  
  
  Identify the differences
&lt;/h2&gt;

&lt;p&gt;Yet, I couldn't help but notice big differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue components are single-file, while Angular separates HTML, CSS and TypeScript into their own files. (I'll probably get used to it.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue focuses on the view layer&lt;/strong&gt;, while Angular comes batteries included: where are my beloved &lt;code&gt;Router&lt;/code&gt; and &lt;code&gt;HttpClient&lt;/code&gt;?!&lt;/li&gt;
&lt;li&gt;Vue doesn't have &lt;strong&gt;modules&lt;/strong&gt;, and instead it has some other concepts such as mixins, filters and transitions.&lt;/li&gt;
&lt;li&gt;There is no concept of &lt;strong&gt;services&lt;/strong&gt; — how am I supposed to abstract logic from components? Is that even a thing in the Vue philosophy?&lt;/li&gt;
&lt;li&gt;If there are no services, then what about state management? Am I forced to resort to Redux/Flux/similar even for smaller apps?&lt;/li&gt;
&lt;li&gt;Where are my &lt;code&gt;Observable&lt;/code&gt;s? It took me so much time to get to know them. What should I use instead of &lt;a href="https://angular.io/guide/rx-library" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these differences gave me the general intuition that &lt;strong&gt;Vue imposes much less on the developer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As someone with an Angular background, I find this a a bit daunting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular's conventions and ways of doing felt secure&lt;/strong&gt;. They also facilitated working with other devs — because we all shared the same practices. How is it going to be with Vue? Does everyone have a different workflow?&lt;/p&gt;

&lt;h2&gt;
  
  
  Look for best practices and popular solutions
&lt;/h2&gt;

&lt;p&gt;So what am I left with? Some stuff looks familiar, other stuff looks better (single-file components look quite slick after all), but there's also some stuff that I miss. And so I am looking for &lt;strong&gt;replacements&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Take &lt;strong&gt;HTTP requests&lt;/strong&gt;: two popular solutions I've seen are &lt;code&gt;axios&lt;/code&gt; and &lt;code&gt;vue-resource&lt;/code&gt;. Looks like a good substitue for &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The issue of &lt;strong&gt;routing&lt;/strong&gt; has also been solved — there's no built-in &lt;code&gt;Router&lt;/code&gt; in Vue, but &lt;a href="https://router.vuejs.org" rel="noopener noreferrer"&gt;vue-router&lt;/a&gt; is a standard plugin that's even suggested when creating a new project via Vue CLI.&lt;/p&gt;

&lt;p&gt;Now, what about &lt;strong&gt;state management&lt;/strong&gt;? Angular has services and I've been lucky enough that, when data binding becomes insufficient, I could use services to store some shared state.&lt;/p&gt;

&lt;p&gt;Vue has no such concept of services, so what's left? A popular option seems to be &lt;a href="https://vuex.vuejs.org" rel="noopener noreferrer"&gt;vuex&lt;/a&gt;, a Flux-inspired state management Vue plugin. I heard about state management before (although mostly through Redux — see &lt;a href="https://medium.freecodecamp.org/understanding-redux-the-worlds-easiest-guide-to-beginning-redux-c695f45546f6" rel="noopener noreferrer"&gt;Understanding Redux&lt;/a&gt;), so I'm interested to dig deeper and see how that turns out. I just hope it won't be too much of a burden.&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%2Fvuex.vuejs.org%2Fvuex.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%2Fvuex.vuejs.org%2Fvuex.png"&gt;&lt;/a&gt;&lt;/p&gt;
The Vuex state management pattern. Looks sensible, but I hope it won't be too heavy. (Source: Vuex docs.)



&lt;p&gt;Lastly — RxJS and &lt;strong&gt;event streaming&lt;/strong&gt;. That's probably the thing I like the most in Angular. Observables make working with streams of events so pleasant and maintainable.&lt;/p&gt;

&lt;p&gt;Well, I haven't found a substitute for that yet. My guess is — there won't be one. Actually, I suppose Vue won't get in my way and I could use RxJS, but there seems to be many built-in features already to ensure reactivity — which I also find quite exciting. We'll see!&lt;/p&gt;

&lt;h2&gt;
  
  
  Enjoy the journey
&lt;/h2&gt;

&lt;p&gt;I used to feel very knowledgeable about Angular, yet Vue has got me back to the position of a beginner. It's not easy, and sure as hell feels uncomfortable.&lt;/p&gt;

&lt;p&gt;But — and this is a message to all striving beginners out there — experience also tells me that &lt;strong&gt;the journey will be worth it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the end of the day, you'll have learnt yet another technology, and be able to do things you couldn't do before. It may take weeks or months, but &lt;strong&gt;it will happen, and you'll feel proud&lt;/strong&gt;. 💪&lt;/p&gt;

&lt;p&gt;As for me, I'm not giving up on Angular just yet, but I'm excited to learn more about Vue. I will focus on building a &lt;strong&gt;mental model&lt;/strong&gt; of how everything fits together in Vue. I look forward to the day when it all just "clicks" — because &lt;strong&gt;there's nothing more exhilarating than pushing your boundaries to learn something new&lt;/strong&gt;. 💻&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you already been in this position? How have you coped with having to re-learn everything? I'd be happy to hear your thoughts!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>vue</category>
      <category>angular</category>
      <category>beginners</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Inbox Zero: How To Keep A Clean Email Inbox (And Mind)</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Thu, 11 Oct 2018 14:01:19 +0000</pubDate>
      <link>https://forem.com/florimondmanca/inbox-zero-how-to-keep-a-clean-email-inbox-and-mind-130h</link>
      <guid>https://forem.com/florimondmanca/inbox-zero-how-to-keep-a-clean-email-inbox-and-mind-130h</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://blog.florimondmanca.com/inbox-zero-how-to-keep-clean-email-inbox-and-mind" rel="noopener noreferrer"&gt;blog.florimondmanca.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I am a final year student in systems and software engineering at &lt;a href="http://www.centralesupelec.fr/en" rel="noopener noreferrer"&gt;CentraleSupélec&lt;/a&gt;. My school has an extremely active campus life. The main channel for communication here is &lt;strong&gt;email&lt;/strong&gt;. Like, &lt;em&gt;mass email&lt;/em&gt;. I mean, &lt;em&gt;30 emails per day, every day&lt;/em&gt;. I'm not even joking. So when I arrived in first year back in 2015, I quickly had to learn &lt;strong&gt;how to manage my inbox efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a former software engineering intern, I had to deal with dozens of email per day too: JIRA notifications, company-wide announcements, CI/CD build alerts, etc.&lt;/p&gt;

&lt;p&gt;I adopted a &lt;strong&gt;simple workflow to keep a clean inbox&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the email irrelevant? Unsubscribe if need be, then delete it.&lt;/li&gt;
&lt;li&gt;Does it require taking action? Do it now, or keep the email until that's done.&lt;/li&gt;
&lt;li&gt;Archive it!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then I discovered this had a name: &lt;strong&gt;Inbox Zero&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most of my posts are technical, but I wanted this one to be non-technical and aim at anyone who has to deal with high-volume inbound email.&lt;/p&gt;

&lt;p&gt;I will share how this method helps me &lt;strong&gt;keep a clean inbox and a peaceful mind&lt;/strong&gt;. I'll show you how easy it is to implement and hopefully inspire you to adopt it as well!&lt;/p&gt;

&lt;h2&gt;
  
  
  What others do
&lt;/h2&gt;

&lt;p&gt;My school of engineering has about 3,000 students, about 500 of which are in my promotion. As a result, I've been able to compare how other people manage large numbers of daily emails.&lt;/p&gt;

&lt;p&gt;I have told friends about how I manage my email. I try to keep my inbox as empty as possible, essentially using it as a todo list. They also told me about their workflow.&lt;/p&gt;

&lt;p&gt;Well, I was shocked to discover that some of them &lt;em&gt;never&lt;/em&gt; cleaned their inbox. &lt;strong&gt;I've seen inboxes containing 7000+ emails&lt;/strong&gt;, some of them from years ago! Downright horrifying. These days, I feel overwhelmed as soon as I've got more than 10 emails in my inbox. How can they bear having thousands of them?&lt;/p&gt;

&lt;p&gt;A few reasons they gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't have the time to clean my inbox.&lt;/li&gt;
&lt;li&gt;I don't need to clean it — I get away with inbox folders.&lt;/li&gt;
&lt;li&gt;I don't know how to clean it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After digging deeper, I also learnt something incredibly surprising: &lt;strong&gt;some people don't know about the "Archive" button!&lt;/strong&gt; They either keep the email in their inbox or delete it.&lt;/p&gt;

&lt;p&gt;Yet, Inbox Zero is &lt;em&gt;all&lt;/em&gt; about the archive button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F267asuaf1iipeoyw89gr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F267asuaf1iipeoyw89gr.jpg" width="500" height="355"&gt;&lt;/a&gt;&lt;/p&gt;
It's as easy as that.



&lt;h2&gt;
  
  
  Email is just like regular mail
&lt;/h2&gt;

&lt;p&gt;Have you ever received mail? Like, actual physical letters? I'm pretty sure you have.&lt;/p&gt;

&lt;p&gt;At least, I have, and I've got a very simple way of dealing with it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I check the mailbox once per day and take the new mail out.&lt;/li&gt;
&lt;li&gt;If it's garbage, I gladly throw it away.&lt;/li&gt;
&lt;li&gt;If it's interesting and I have to do something about it (like fill in an online form, or notify someone):

&lt;ul&gt;
&lt;li&gt;Can I do it within 2 minutes? If so, I do it right away.&lt;/li&gt;
&lt;li&gt;Otherwise, I keep the mail front and center on a shelf so I am reminded to deal with it when I've got the time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When I'm done, I either keep the letter in my records for future reference or toss it away into the waste paper basket of oblivion (how poetic is that?).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm pretty sure that's mostly how you do it too. You don't even have to think about it too hard. It's &lt;em&gt;natural&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Yet, I have a question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Would you let letters, postcards and ads pile up in your physical mailbox forever, even after they've been been read and dealt with?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds absurd, right? Yet, &lt;strong&gt;this is exactly what you do when you keep email in your inbox&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  I have good news
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The ideas behind Inbox Zero are simple&lt;/strong&gt;. It's no rocket science. There's no black magic. Even better:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Inbox Zero isn't any different from how you're used to manage physical mail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let me repeat — it's the exact same workflow! You've been doing it all your life. &lt;strong&gt;You know how it works&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So there is absolutely &lt;em&gt;reason zero&lt;/em&gt; (pun intended) for not dealing with electronic email the same way.&lt;/p&gt;

&lt;p&gt;Still feeling a bit unsure? &lt;em&gt;Let me show you da wae&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The way to a clean inbox
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. Start by archiving all your inbox. Now.
&lt;/h4&gt;

&lt;p&gt;Here you are with hundreds or thousands of emails in your inbox. How can you make sense of all that mess?&lt;/p&gt;

&lt;p&gt;Well, I've already told you: &lt;strong&gt;the core of Inbox Zero is two words&lt;/strong&gt; — &lt;strong&gt;just archive!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contrary to trash, archived emails are not lost. They just go out of your inbox, and into somewhere you can retrieve them in the future. That's why my advice is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should feel confident about archiving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first step should then be: &lt;strong&gt;archive all emails presently in your inbox&lt;/strong&gt;. Tested and proven by fellow students and friends.&lt;/p&gt;

&lt;p&gt;And believe me, it's pretty relieving.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1roebtisv557os739un.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1roebtisv557os739un.gif" width="480" height="356"&gt;&lt;/a&gt;&lt;/p&gt;
Gladly archive all your inbox. 👌 (Now.)



&lt;h4&gt;
  
  
  2. Deal with irrelevant incoming emails
&lt;/h4&gt;

&lt;p&gt;Is your inbox filling up with tons of ads or content you don't even care about? You need to sort this out.&lt;/p&gt;

&lt;p&gt;If it's a subscription of some sort, &lt;strong&gt;find and click the unsubscribe link&lt;/strong&gt;. Then only should you delete the email.&lt;/p&gt;

&lt;p&gt;If the email is a one-off, then the only thing you can do is delete it.&lt;/p&gt;

&lt;p&gt;The important thing is that &lt;strong&gt;you should decide immediately whether to toss an incoming email away&lt;/strong&gt; so that garbage doesn't end up filling up your inbox again.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Ponder emails requiring action
&lt;/h4&gt;

&lt;p&gt;Sometimes, an email requires you to take action. It may be forwarding it to someone else, answering a meeting request, filling in an online form — anything.&lt;/p&gt;

&lt;p&gt;When that is the case, there are only 2 options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can the action be done quickly, e.g. under 2 minutes? Then, just do it!&lt;/li&gt;
&lt;li&gt;Otherwise, you should keep the email in your inbox and work it out it later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The good thing about option 2 is that &lt;strong&gt;any read email in your inbox can then be thought of as a todo&lt;/strong&gt;. That's also why I sometimes call Inbox Zero "Inbox as a Todo".&lt;/p&gt;

&lt;p&gt;Yet, if you notice these "todo" emails pile up in your inbox, it might be worth investing in a task management tool, or a todo app of some sort (or get stuff done in the first place 😉). That's an even shorter path to a clean inbox. Plus, if it has reminders and alerts, you'll probably find it easier on your mind as well!&lt;/p&gt;

&lt;p&gt;As an example, here is an email I would certainly take action upon right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;From: elsa@company.biz
To: florimond@company.biz
Subject: Spare paper
---
Hi Florimond,

Do you have any spare paper left in the cupboard?
I need 2 sheets of paper for a presentation this afternoon.

Cheers,
Elsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That definitely won't need more than 2 minutes. I can just check whether I have some paper left and answer to Elsa that she can pick it up at my desk anytime she wants.&lt;/p&gt;

&lt;p&gt;Here's another example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;From: marketing@company.biz
To: everyone-including-me@company.biz
Subject: Rebranding Survey
---
Hi all,

We are working on rebranding our company and would like to gather everyone's input and ideas.

Please fill in this online form when you have some time: [Link]

Best,
Marketing Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;em&gt;will&lt;/em&gt; take more than 2 minutes to deal with. I probably want to give some actionable input to marketing as to how we can improve our brand and what values it should convey. As a result, I will keep this email in my inbox and anytime I check my email again — which might be when I have some free time — I will be reminded to answer the survey.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Just. Archive. It.
&lt;/h4&gt;

&lt;p&gt;In any case, if the email is of any importance to you, you should keep it. By that I mean, &lt;strong&gt;archive it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The reason why is simple: if you ever need that email again, you'll find it in the archives.&lt;/p&gt;

&lt;p&gt;On the other hand, if you'll never need it again, there's no reason to waste storage for it and you should throw it away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free up your mind
&lt;/h2&gt;

&lt;p&gt;We sometimes think of email as a burden. We need to "deal with it" or "sort it out" multiple times a day. It shouldn't be like that. It doesn't &lt;em&gt;have&lt;/em&gt; to be like that.&lt;/p&gt;

&lt;p&gt;On a personal level, I strongly believe that Inbox Zero has helped me &lt;strong&gt;free up my mind&lt;/strong&gt;. It helped me &lt;strong&gt;reduce stress&lt;/strong&gt; because I always know that things I have to do are in my inbox, and I can just look them up. It's a matter of &lt;strong&gt;reducing cognitive load&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if you're a developer, there's a chance you're already facing a fair amount of cognitive load in your regular job. It only makes managing your email smoothly even more important.&lt;/p&gt;

&lt;p&gt;If you try out Inbox Zero — let me know how it turns out! A few fellow students who went for it told me they love how easy it is to manage email now. Hope you reconcile with email too. 💻&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tips</category>
    </item>
    <item>
      <title>Reconciling Dataclasses And Properties In Python</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Mon, 08 Oct 2018 06:14:21 +0000</pubDate>
      <link>https://forem.com/florimondmanca/reconciling-dataclasses-and-properties-in-python-4ogc</link>
      <guid>https://forem.com/florimondmanca/reconciling-dataclasses-and-properties-in-python-4ogc</guid>
      <description>&lt;p&gt;&lt;small&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.florimondmanca.com/reconciling-dataclasses-and-properties-in-python" rel="noopener noreferrer"&gt;blog.florimondmanca.com&lt;/a&gt; on Oct 6, 2018.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you can't wait for the TL;DR, jump to Lessons Learned at the end of this article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been playing around with Python 3.7's &lt;a href="https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass" rel="noopener noreferrer"&gt;dataclasses&lt;/a&gt; — and so far they've been super awesome. For the most part, they're quite intuitive and easy to use and customize.&lt;/p&gt;

&lt;p&gt;Yet, there's one thing I've been struggling with and couldn't find online help for, which is &lt;strong&gt;implementing Python properties on dataclasses&lt;/strong&gt;. And chances are — I am not alone.&lt;/p&gt;

&lt;p&gt;That's why I decided to solve the problem once and for all. I wanted to figure out &lt;strong&gt;how to reconcile dataclasses and properties&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I sat down before my computer, fired up an interpreter and wrote down my thought process as I made my way towards a solution. After a night of trial and error and some wording enhancements, the result is this very blog post!&lt;/p&gt;

&lt;p&gt;As a pleasant side effect, you'll get to &lt;strong&gt;see many features of dataclasses in action&lt;/strong&gt;. I'll start with a quick overview of dataclasses, and we'll get to the code right after that.&lt;/p&gt;

&lt;p&gt;Of course, you'll find all the code supporting this post on &lt;a href="https://github.com/florimondmanca/dataclasses-properties" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataclasses: a 10,000-ft overview
&lt;/h2&gt;

&lt;p&gt;Dataclasses are, simply put, &lt;strong&gt;classes made to hold data&lt;/strong&gt;. Their specification in &lt;a href="https://www.python.org/dev/peps/pep-0557/#rationale" rel="noopener noreferrer"&gt;PEP-557&lt;/a&gt; was motivated by the fact that a lot of classes we write are merely used as editable data containers. When that happens, we spend time writing boilerplate code which most often results in an ugly &lt;code&gt;__init__()&lt;/code&gt; method with tons of arguments and just as many lines for storing them as attributes — not to speak about handling &lt;a href="https://dev.to/florimondmanca/python-mutable-defaults-are-the-source-of-all-evil-6kk"&gt;default arguments&lt;/a&gt;…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbldjs5x3d7z4wx2vwbz7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbldjs5x3d7z4wx2vwbz7.gif" width="478" height="266"&gt;&lt;/a&gt;&lt;/p&gt;
Dataclasses, featuring Raymond Hettinger in a GIF.



&lt;p&gt;And there is! The answer is: &lt;strong&gt;dataclasses&lt;/strong&gt;. 🎉&lt;/p&gt;

&lt;p&gt;Python implements dataclasses in the well-named &lt;a href="https://docs.python.org/3/library/dataclasses.html" rel="noopener noreferrer"&gt;dataclasses&lt;/a&gt; module, whose superstar is the &lt;code&gt;@dataclass&lt;/code&gt; decorator. This decorator is really just a &lt;strong&gt;code generator&lt;/strong&gt;. It takes advantage of Python's type annotations (if you still don't use them, &lt;a href="https://dev.to/florimondmanca/why-i-started-using-python-type-annotations--and-why-you-should-too-2a3m"&gt;you really should&lt;/a&gt;) to &lt;strong&gt;automatically generate boilerplate code&lt;/strong&gt; you'd have to mechanically type yourself otherwise.&lt;/p&gt;

&lt;p&gt;As a point of comparison, here's how you would create a &lt;code&gt;Vehicle&lt;/code&gt; class with a &lt;code&gt;wheels&lt;/code&gt; attribute using a regular class declaration:&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="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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing fancy, really. Now, the &lt;code&gt;@dataclass&lt;/code&gt; version:&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;# 0_initial.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Believe it or not — these two code snippets are strictly equivalent! It's actually a win, because beyond &lt;code&gt;__init__()&lt;/code&gt;,  &lt;code&gt;@dataclass&lt;/code&gt; generates a bunch of extra stuff for free, including a handsome &lt;code&gt;__repr__()&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&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="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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;
&lt;span class="nc"&gt;Vehicle&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In short, dataclasses are a simple, elegant, Pythonic way of creating classes that hold data. 🐍&lt;/p&gt;

&lt;p&gt;But…&lt;/p&gt;

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

&lt;p&gt;I sometimes resort to the &lt;code&gt;@property&lt;/code&gt; decorator to implement specific logic when getting/setting an attribute. That's really just the Pythonic way of implementing getters and setters.&lt;/p&gt;

&lt;p&gt;Building upon the previous &lt;code&gt;Vehicle&lt;/code&gt; class, I would make the &lt;code&gt;wheels&lt;/code&gt; attribute private and put an &lt;code&gt;@property&lt;/code&gt; on top of 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="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="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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;_wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
        &lt;span class="c1"&gt;# note the underscore — it's now private! 👻
&lt;/span&gt;
    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;getting wheels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_wheels&lt;/span&gt;

    &lt;span class="nd"&gt;@wheels.setter&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;setting wheels to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wheels&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what it 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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&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="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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&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;3&lt;/span&gt;
&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the question is — &lt;strong&gt;how can I implement such a property on a dataclass?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, so &lt;em&gt;this&lt;/em&gt; is the problem?
&lt;/h2&gt;

&lt;p&gt;You may think this to be a trivial question. Mind you, &lt;strong&gt;it is not trivial&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dataclasses generate the &lt;code&gt;__init__()&lt;/code&gt; method for you — and that's great. They even provide a &lt;code&gt;__post_init__()&lt;/code&gt; hook method in case you want to do some more initialization (see &lt;a href="https://docs.python.org/3/library/dataclasses.html#post-init-processing" rel="noopener noreferrer"&gt;Post-init processing&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;However, this means you cannot do the same trick as with normal classes, i.e. storing a public-looking argument (e.g. &lt;code&gt;wheels&lt;/code&gt;) into a private attribute (&lt;code&gt;_wheels&lt;/code&gt;) that you'll build an &lt;code&gt;@property&lt;/code&gt; out of.&lt;/p&gt;

&lt;p&gt;That's where the problem comes from. And to be honest, it gave me a bit of a headache.&lt;/p&gt;

&lt;p&gt;Because I think the problem solving was interesting, I'll take you through 5 consecutive attempts to correctly implement that property on a dataclass version of the &lt;code&gt;Vehicle&lt;/code&gt; class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: declaring a private field
&lt;/h2&gt;

&lt;p&gt;First, let's keep things simple. We want to store &lt;code&gt;wheels&lt;/code&gt; in a private field and use it in the &lt;code&gt;@property&lt;/code&gt;, right? So why not simply declare a &lt;code&gt;_wheels&lt;/code&gt; field on the dataclass?&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;# 1_private_field.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;_wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

    &lt;span class="c1"&gt;# wheels @property as before
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, this won't work — otherwise this blog post wouldn't be of much use! 😙&lt;/p&gt;

&lt;p&gt;The reason why is because the constructor now expects a &lt;code&gt;_wheels&lt;/code&gt; argument instead of &lt;code&gt;wheels&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&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="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="nc"&gt;Traceback &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;input&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="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c9de8fb1422&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;----&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;car&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="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="nb"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&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;got&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;unexpected&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="n"&gt;argument&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be fair, that's just &lt;code&gt;@dataclass&lt;/code&gt; doing its job. Still, that's not what we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: make use of &lt;code&gt;InitVar&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you read through the documentation, you'll learn that &lt;code&gt;InitVar&lt;/code&gt; allows you to implement &lt;a href="https://docs.python.org/3/library/dataclasses.html#init-only-variables" rel="noopener noreferrer"&gt;init-only variables&lt;/a&gt;. These variables can be passed to the constructor, but won't be stored in an attribute on the class. Instead, the variable is passed as an argument to &lt;code&gt;__post_init__()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why not use this to create an init-only &lt;code&gt;wheels&lt;/code&gt; variable and store that in a &lt;code&gt;_wheels&lt;/code&gt; field? We just need to give the latter a default (e.g. &lt;code&gt;None&lt;/code&gt;) so that it is not required by the constructor:&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;# 2_initvar.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InitVar&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InitVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;_wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# default given =&amp;gt; not required in __init__()
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__post_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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;_wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;

    &lt;span class="c1"&gt;# wheels @property as before
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Granted, &lt;code&gt;__init__()&lt;/code&gt; now expects a &lt;code&gt;wheels&lt;/code&gt; argument instead of &lt;code&gt;_wheels&lt;/code&gt;, which is what we want.&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;@dataclass&lt;/code&gt; now generates other boilerplate code and magic methods using &lt;code&gt;_wheels&lt;/code&gt;, which is problematic.&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;car&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="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;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;
&lt;span class="nc"&gt;Vehicle&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="c1"&gt;# 😕
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Attempt 3: make use of &lt;code&gt;field()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Digging deeper into the docs, I found that one could fine-tune the field generation behavior using the &lt;a href="https://docs.python.org/3/library/dataclasses.html#dataclasses.field" rel="noopener noreferrer"&gt;field()&lt;/a&gt; function. You can pass it a &lt;code&gt;default&lt;/code&gt; value and it accepts a &lt;code&gt;repr&lt;/code&gt; argument to control whether the field should be included in the generated &lt;code&gt;__repr__()&lt;/code&gt;. Here's how it looks when used on &lt;code&gt;_wheels&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 3_field.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InitVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;_wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&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="nb"&gt;repr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# __post_init__() as before
&lt;/span&gt;    &lt;span class="c1"&gt;# wheels @property as before
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet — we don't have &lt;code&gt;_wheels&lt;/code&gt; included in &lt;code&gt;__repr__()&lt;/code&gt; anymore. But we still don't have &lt;code&gt;wheels&lt;/code&gt; either!&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;car&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="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;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;
&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Where is `wheels=4`? 😕😕😕
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Attempt 4: make &lt;code&gt;wheels&lt;/code&gt; a proper field
&lt;/h2&gt;

&lt;p&gt;In the previous attempts, &lt;code&gt;wheels&lt;/code&gt; was an &lt;code&gt;InitVar&lt;/code&gt; — not a field. This time, let's declare it as a field in its own right. It will be possible to pass it in the constructor, and it should be included in &lt;code&gt;__repr__()&lt;/code&gt; this time around.&lt;/p&gt;

&lt;p&gt;The good thing is, the &lt;code&gt;@property&lt;/code&gt; definition of &lt;code&gt;wheels&lt;/code&gt; declared later in the class will not interfere with &lt;code&gt;@dataclass&lt;/code&gt;'s generation process — &lt;em&gt;because it is not a type annotation&lt;/em&gt;, which is what &lt;code&gt;@dataclass&lt;/code&gt; relies on to generate the fields.&lt;/p&gt;

&lt;p&gt;That might start to be a bit complicated, so let me show you some code. I'll reproduce the &lt;code&gt;@property&lt;/code&gt; for &lt;code&gt;wheels&lt;/code&gt; in full this time:&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;# 4_wheels_field.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;  &lt;span class="c1"&gt;# Now a regular dataclass field
&lt;/span&gt;
    &lt;span class="c1"&gt;# The rest just as before:
&lt;/span&gt;
    &lt;span class="n"&gt;_wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&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="nb"&gt;repr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__post_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="c1"&gt;# Note: wheels is not passed as an argument
&lt;/span&gt;        &lt;span class="c1"&gt;# here anymore, because it is not an
&lt;/span&gt;        &lt;span class="c1"&gt;# `InitVar` anymore.
&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt;  &lt;span class="c1"&gt;# (1)
&lt;/span&gt;
    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;getting wheels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_wheels&lt;/span&gt;

    &lt;span class="nd"&gt;@wheels.setter&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;setting wheels to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wheels&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like we're getting there, aren't we?&lt;/p&gt;

&lt;p&gt;Unfortunately, not quite. 😞 There's a catch in this implementation.&lt;/p&gt;

&lt;p&gt;Indeed, you may think line &lt;code&gt;(1)&lt;/code&gt; puts the value of &lt;code&gt;wheels&lt;/code&gt; that was given to the constructor (and stored into &lt;code&gt;self.wheels&lt;/code&gt;) into &lt;code&gt;_wheels&lt;/code&gt;. For example, calling &lt;code&gt;Vehicle(wheels=4)&lt;/code&gt; would result in having &lt;code&gt;_wheels == 4&lt;/code&gt;. Sadly, that is not the case!&lt;/p&gt;

&lt;p&gt;Here's why: when executing &lt;code&gt;__post_init__()&lt;/code&gt;, &lt;code&gt;self.wheels&lt;/code&gt; is the value returned by the &lt;code&gt;wheels&lt;/code&gt; property's getter — &lt;em&gt;not&lt;/em&gt; the value initially stored during &lt;code&gt;__init__()&lt;/code&gt;! And that getter returns &lt;code&gt;self._wheels&lt;/code&gt;, which is &lt;code&gt;None&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;I know, it's getting all tangled up, but please bear with me:&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;car&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="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;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;  &lt;span class="c1"&gt;# hint: this is (1) being executed
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# nope, nothing in there…
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you think about it, what we're doing in (1) is just replacing &lt;code&gt;_wheels&lt;/code&gt; with its own value. Quite useless, if you ask me. We would actually get the same result if we didn't even implement &lt;code&gt;__post_init__()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Duh! So what can we do? 😩&lt;/p&gt;

&lt;p&gt;Fortunately, there's hope!&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 5: exclude &lt;code&gt;_wheels&lt;/code&gt; from the constructor
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Let me warn you — this fifth and final attempt will work, and the reason why, which I'll explain in a minute, is outrageous.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this point, you'd be right to feel sad — I felt sad myself. But fear not! There is one thing from the documentation that we haven't tried yet.&lt;/p&gt;

&lt;p&gt;So far, the &lt;code&gt;_wheels&lt;/code&gt; attribute has been declared using &lt;code&gt;field(default=None, repr=False)&lt;/code&gt;. Using &lt;code&gt;default=None&lt;/code&gt; here means that we are able to omit passing a value for &lt;code&gt;_wheels&lt;/code&gt; in the constructor — it will be given the value of &lt;code&gt;None&lt;/code&gt; during &lt;code&gt;__init__()&lt;/code&gt;. However, it is still possible to give it a value in the constructor, and everything will work as expected:&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;car&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="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;_wheels&lt;/span&gt;&lt;span class="o"&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;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, how about we find a way to remove the &lt;code&gt;_wheels&lt;/code&gt; argument from the constructor? Will it solve our problem? (&lt;em&gt;Spoiler alert: it will.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Guess what: &lt;code&gt;field()&lt;/code&gt; accepts an &lt;code&gt;init&lt;/code&gt; argument for that exact purpose. The docs on &lt;code&gt;field()&lt;/code&gt; read:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt;: If true (the default), this field is included as a parameter to the generated &lt;code&gt;__init__&lt;/code&gt; method. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds trivial, right? Well, let's try using it on &lt;code&gt;_wheels&lt;/code&gt; (I removed the &lt;code&gt;__post_init__()&lt;/code&gt; hook because we previously showed that it was actually useless):&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;# 5_init_false.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&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="n"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;_wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;getting wheels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_wheels&lt;/span&gt;

    &lt;span class="nd"&gt;@wheels.setter&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;setting wheels to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wheels&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, guess what? &lt;strong&gt;This has just solved all of our problems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because we used &lt;code&gt;init=False&lt;/code&gt;, the constructor generated by &lt;code&gt;@dataclass&lt;/code&gt; will not initialize &lt;code&gt;_wheels&lt;/code&gt; at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, it &lt;em&gt;will&lt;/em&gt; initialize &lt;code&gt;wheels&lt;/code&gt; with the value passed to the constructor. If we could extract the generated code, one of the instructions in &lt;code&gt;__init__()&lt;/code&gt; would 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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you tell me — what does this execute exactly?&lt;/p&gt;

&lt;p&gt;Yep, that's right. It will execute the setter! 🙀&lt;/p&gt;

&lt;p&gt;Look! We've actually been seeing the print statement in the setter since attempt 4!&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;car&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="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;setting&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;  &lt;span class="c1"&gt;# the `wheels` setter being called
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me remind you of the code for that setter:&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;@wheels.setter&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;setting wheels to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wheels&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sets the value of &lt;code&gt;_wheels&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;As a result, the value for &lt;code&gt;wheels&lt;/code&gt; passed in the constructor is put into &lt;code&gt;_wheels&lt;/code&gt; — and nowhere else, because after the class has been generated, &lt;code&gt;wheels&lt;/code&gt; only refers to the &lt;code&gt;@property&lt;/code&gt;, not to a field on the dataclass.&lt;/p&gt;

&lt;p&gt;If you think about it, this is exactly what we were doing when implementing the property on good ol' regular classes. Remember?&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="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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;_wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
        &lt;span class="c1"&gt;# This is equivalent to calling:
&lt;/span&gt;        &lt;span class="c1"&gt;# `self.wheels = wheels`
&lt;/span&gt;        &lt;span class="c1"&gt;# which *is* what the __init__() method
&lt;/span&gt;        &lt;span class="c1"&gt;# now generated by @dataclass actually does.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caveat: &lt;strong&gt;this approach only holds because the property's setter is implemented&lt;/strong&gt;. If we only implemented a getter (i.e. to make a read-only field), the &lt;code&gt;__init__()&lt;/code&gt; method wouldn't be able to assign the attribute and would crash. This is intended behavior, though, because &lt;strong&gt;dataclasses were designed to be editable data containers&lt;/strong&gt;. If you really need read-only fields, you shouldn't be resorting to dataclasses in the first place. Perhaps &lt;code&gt;NamedTuple&lt;/code&gt;s would be a viable alternative — they are the read-only equivalent of dataclasses.&lt;/p&gt;

&lt;p&gt;Anyway, long story short…&lt;/p&gt;

&lt;h2&gt;
  
  
  Success!
&lt;/h2&gt;

&lt;p&gt;We have successfully implemented properties on dataclasses. 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jgmiheawy8g5dc3s3o5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jgmiheawy8g5dc3s3o5.jpg" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
Memes will never let you down.



&lt;p&gt;To be honest, &lt;strong&gt;it was surprisingly not easy&lt;/strong&gt;. We've been through five different attempts, navigating through the documentation and painstakingly coding our way towards dataclass properties.&lt;/p&gt;

&lt;p&gt;So after all this hassle, can we at least derive &lt;strong&gt;a quick recipe for implementing properties on dataclasses&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;The answer is: yes, we can. ✌️&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;Have you noticed a pattern between using an &lt;code&gt;@property&lt;/code&gt; on a regular class vs. on a dataclass?&lt;/p&gt;

&lt;p&gt;Look, here's the regular class version:&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="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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;_wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_wheels&lt;/span&gt;

    &lt;span class="nd"&gt;@wheels.setter&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wheels&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="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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;_wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the dataclass version, using a diff syntax to highlight the differences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ from dataclasses import dataclass, field
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ @dataclass
&lt;/span&gt;  class Vehicle:
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     wheels: int
+     _wheels: field(init=False, repr=False)
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     def __init__(self, wheels: int):
-         self._wheels = wheels
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      @property
      def wheels(self) -&amp;gt; int:
          return self._wheels
&lt;span class="err"&gt;
&lt;/span&gt;      @wheels.setter
      def wheels(self, wheels: int):
          self._wheels = wheels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Written in plain words, for you litterature freaks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How to implement a property on a dataclass:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove &lt;code&gt;__init__()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Declare the property as a field&lt;/li&gt;
&lt;li&gt;Add an associated private variable using &lt;code&gt;field(init=False, repr=False)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Way to go!
&lt;/h2&gt;

&lt;p&gt;If you managed to read up to here, congratulations! This blog post dealt with a highly specific and technical topic, yet I've had a lot of fun writing it and figuring out &lt;strong&gt;how to implement dataclass properties&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For those wondering — the series on Apache Kafka still goes on! I figured a small break never hurts, and I felt like it gave me the opportunity to write something spontaneous.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post and, as mentioned in introduction, you can find all the code on &lt;a href="https://github.com/florimondmanca/dataclasses-properties" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. See you next time! 💻&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>python</category>
      <category>dataclasses</category>
      <category>devtips</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Breaking News: Everything Is An Event! (Streams, Kafka And You)</title>
      <dc:creator>Florimond Manca</dc:creator>
      <pubDate>Mon, 01 Oct 2018 19:51:36 +0000</pubDate>
      <link>https://forem.com/florimondmanca/breaking-news-everything-is-an-event-streams-kafka-and-you-2n9j</link>
      <guid>https://forem.com/florimondmanca/breaking-news-everything-is-an-event-streams-kafka-and-you-2n9j</guid>
      <description>&lt;p&gt;Welcome back! Here's the third article in my series on building streaming apps with &lt;strong&gt;Apache Kafka&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/florimondmanca/building-a-streaming-fraud-detection-system-with-kafka-and-python-neg"&gt;previous article&lt;/a&gt; had us build our first real-world streaming application — a real-time fraud detection system.&lt;/p&gt;

&lt;p&gt;This time, we'll take a deeper look at some &lt;strong&gt;core concepts&lt;/strong&gt; behind Apache Kafka.&lt;/p&gt;

&lt;p&gt;More specifically, we'll try to &lt;strong&gt;perform a mind shift&lt;/strong&gt;. We are used to think in terms of state, but thinking in terms of &lt;strong&gt;events&lt;/strong&gt; allows us to enter the realm of &lt;strong&gt;streaming data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then, after introducing &lt;strong&gt;streams&lt;/strong&gt; and the &lt;strong&gt;streaming data paradigm&lt;/strong&gt; in greater detail, we'll take a moment to think about why it might be important you become knowledgeable about it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spoiler alert: because it's fun to learn, reusable, marketable, and useful to build reactive applications businesses.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Without further ado, let me start your introduction to &lt;strong&gt;streaming data&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  State and requests
&lt;/h2&gt;

&lt;p&gt;As developers who build apps, data pipelines and data-driven systems, we are used to think in terms of &lt;strong&gt;state&lt;/strong&gt;. We often ask questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Hi, Mailer microservice! Frontend app speaking. Could you send me the list of processed emails for User A so I can update their dashboard?"&lt;/li&gt;
&lt;li&gt;"Hi, REST API! Mailer microservice here. Would you send me the current list of posts, so I can diff it with last week's list and send an update to newsletter subscribers?"&lt;/li&gt;
&lt;li&gt;"Hi, SQL database! I'm the application server. What rows do I have in the &lt;code&gt;posts&lt;/code&gt; table?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We build complex systems made of many different components and try to wire them up through &lt;strong&gt;requests&lt;/strong&gt;, (or queries, or any communication fitting the request/response paradigm).&lt;/p&gt;

&lt;p&gt;Even when we use the latest and shiniest architecture design principles (hint: microservices), we run the risk of ending up with a &lt;strong&gt;tightly coupled architecture&lt;/strong&gt; because components need to know about each other's API in great detail, or need to keep polling one another to detect &lt;strong&gt;changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fmicroservice-blog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fmicroservice-blog.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
Your typical microservices web application. See that soup of requests? Can we do better?



&lt;h2&gt;
  
  
  Introducing events
&lt;/h2&gt;

&lt;h3&gt;
  
  
  There must be a better way
&lt;/h3&gt;

&lt;p&gt;Database queries, API HTTP requests, cache lookups — as far as this article is concerned, all of these are &lt;strong&gt;requests&lt;/strong&gt; that aim at retrieving state. But what do you think the following are?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User created
Tag followed
Post published
Email processed
Draft submitted
Profile updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not requests. These also have nothing to do about state.&lt;/p&gt;

&lt;p&gt;This is a plain and simple list of &lt;strong&gt;events&lt;/strong&gt; — mere &lt;strong&gt;facts&lt;/strong&gt; about what just happened in our application or in the outside world. 🏷&lt;/p&gt;

&lt;p&gt;We don't always realise that the data we retrieve from storage (like a database or a cache) is only there because &lt;strong&gt;something happened in the first place&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That &lt;em&gt;something&lt;/em&gt; is an event. But we'll get to it in a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does an event look like?
&lt;/h3&gt;

&lt;p&gt;An event is a general, abstract concept. One helpful dictionary definition is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Event&lt;/strong&gt;: a thing that happens or takes place, especially one of importance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It may be useful at this point to give you a &lt;strong&gt;concrete intuition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In software, an event is essentially made of two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;strong&gt;action&lt;/strong&gt; (or label, name, code…) used to clearly identify what happened. For example, &lt;code&gt;'user_created'&lt;/code&gt; or &lt;code&gt;'email_processed'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt; that provide &lt;strong&gt;details&lt;/strong&gt; and fully determines the event. For example, the first and last name of a newly created user, or the ID of a processed email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, here's one of many possible representations (in JSON) of an event stating that "User 1343 had they first name updated to 'Bob'":&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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user_updated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"context"&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;1343&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"previous"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Robert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"new"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, an event is simply &lt;strong&gt;a fact describing something that happened in the universe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While seemingly trivial, this idea is at the core of a entirely new &lt;strong&gt;paradigm&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The brutal truth
&lt;/h3&gt;

&lt;p&gt;There's a lot to say about events and how they mean and imply different things depending on the field of computing you're working in.&lt;/p&gt;

&lt;p&gt;But in my opinion, here's the &lt;strong&gt;hard and brutal truth&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Everything is an event&lt;/strong&gt;, and our ability to harness them is critical to building reactive and performant applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're not convinced yet, let me give you some examples based on real-world apps you probably already know about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arguments from the real world
&lt;/h3&gt;

&lt;p&gt;What do you think applications such as &lt;strong&gt;LinkedIn&lt;/strong&gt; and &lt;strong&gt;Slack&lt;/strong&gt; (no advertising intended) have in common?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All of them produce, harness and process events&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Take LinkedIn for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New job offer? That's an event.&lt;/li&gt;
&lt;li&gt;A notification? That's an event too.&lt;/li&gt;
&lt;li&gt;A profile view? Still an event.&lt;/li&gt;
&lt;li&gt;You updated a skill or your resume? Yet another event!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How about Slack? Well…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new direct message? Yup, that's an event.&lt;/li&gt;
&lt;li&gt;Someone created a channel in a workspace? Another event.&lt;/li&gt;
&lt;li&gt;Logged into Slack? Heck — even that is an event!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anything that LinkedIn or Slack do is process those events in order to create value (whatever that means) to their users. Notice how events can also form a &lt;strong&gt;chain&lt;/strong&gt;: for example, a &lt;code&gt;'direct_message_received'&lt;/code&gt; event will probably trigger a &lt;code&gt;'notification_created'&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;Anyway, the core idea is that &lt;strong&gt;every single thing that happens within an app is an event.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the beginning of this article, I told you that requests (in the general sense) allow to retrieve state. So if everything is an event, &lt;strong&gt;where does state come from?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  State, a.k.a persisted events
&lt;/h3&gt;

&lt;p&gt;Events occur at a given point in time. They are &lt;strong&gt;ephemeral&lt;/strong&gt; by nature.&lt;/p&gt;

&lt;p&gt;This means that events need to be &lt;strong&gt;persisted&lt;/strong&gt; so you can access them later, i.e. after they happened.&lt;/p&gt;

&lt;p&gt;For example, notifications need to be stored in database so you can read them again by the click of a menu.&lt;/p&gt;

&lt;p&gt;It turns out that &lt;strong&gt;once events are processed and persisted, they become state&lt;/strong&gt;. From then on, you can retrieve them (or some transformation or aggregation of them) using regular requests.&lt;/p&gt;

&lt;p&gt;Let's take an example. For those of you who use Twitter, have you ever noticed how a "Like" you received via a notification may not be counted on the tweet's page until after a few seconds have passed? That's a &lt;code&gt;'tweet_liked'&lt;/code&gt; event being persisted into state — a tweet's counter of likes!&lt;/p&gt;

&lt;p&gt;At this point, you may start to realise that, in a sense, &lt;strong&gt;state is the consequence of events&lt;/strong&gt;. An event occurs, you catch it, process it, store it and update state. That's all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Say "Hello" to streaming data!
&lt;/h2&gt;

&lt;p&gt;When I accepted the truth that everything can be considered as an event, and state simply derives from them, I began to realise there is a &lt;strong&gt;completely different paradigm&lt;/strong&gt; at play here.&lt;/p&gt;

&lt;p&gt;This paradigm is that of &lt;strong&gt;streaming data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of thinking about systems exchanging state through requests and response, I started to think about systems exchanging &lt;strong&gt;state changes through streams of events&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a stream?
&lt;/h3&gt;

&lt;p&gt;Streams have a particular definition in computing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stream&lt;/strong&gt;: a continuous flow of data or instructions, especially one having a constant or predictable rate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, I find this definition to be too general. In the context of &lt;strong&gt;event streams&lt;/strong&gt;, we can boil it down to a more specific and more concise form. Here's what I ended up with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;stream&lt;/strong&gt; is an unbounded, time-ordered collection of events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Graphical representations always help, don't they? Here's how you can represent a stream of events as a set of points arranged in a timeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fstream.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fstream.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
Simple representation of a stream.



&lt;p&gt;To give you an even better intuition of what streams are and what you can do with them, I'd like to take you through an analogy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The river analogy
&lt;/h3&gt;

&lt;p&gt;Although streams have their own definition in computer science, in everyday language, a stream generally refers to a small river, or at least a flow of liquid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1527489377706-5bf97e608852%3Fixlib%3Drb-0.3.5%26ixid%3DeyJhcHBfaWQiOjEyMDd9%26s%3D30e71ee8757d4448721e27ea96208156%26auto%3Dformat%26fit%3Dcrop%26w%3D1127%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1527489377706-5bf97e608852%3Fixlib%3Drb-0.3.5%26ixid%3DeyJhcHBfaWQiOjEyMDd9%26s%3D30e71ee8757d4448721e27ea96208156%26auto%3Dformat%26fit%3Dcrop%26w%3D1127%26q%3D80" width="1127" height="682"&gt;&lt;/a&gt;&lt;/p&gt;
Time-lapse photography of river. @the_bracketeer, unsplash.com



&lt;p&gt;Image you walk down the riverside. You stop somewhere and watch the water flow past you. There are a lot of observations you can make that have an event stream equivalent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Water flows at a rather steady rate → streams are a &lt;strong&gt;continuous flow of events&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Yet, rock formations may locally vary the flow rate → &lt;strong&gt;processing rate can vary&lt;/strong&gt; at various stages of a stream processing pipeline.&lt;/li&gt;
&lt;li&gt;You can count the number of fish swimming in the river → events can be &lt;strong&gt;persisted&lt;/strong&gt; and become state.&lt;/li&gt;
&lt;li&gt;The river results from mulitple, small upstream brooks combined together → streams can have &lt;strong&gt;multiple inputs and outputs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Large tree trunks retain debris, leaving a clean water downstream → streams can be manipulated through &lt;strong&gt;transformations&lt;/strong&gt; (such as filtering, mapping, etc.).&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like this analogy because it gives a general, lively overview of event streams.&lt;/p&gt;

&lt;p&gt;But wait a second. So far, we've been talking about streams but didn't say much about how it all fitted within the Apache Kafka ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  How are streams implemented in Apache Kafka?
&lt;/h3&gt;

&lt;p&gt;Kafka calls itself a &lt;strong&gt;distributed streaming platform&lt;/strong&gt; and implements the streaming data paradigm perfectly. It provides simple yet extremely powerful &lt;strong&gt;abstractions&lt;/strong&gt; to deal with streams of events in a reliable, fault-tolerant manner.&lt;/p&gt;

&lt;p&gt;In Kafka, the equivalent of a stream of events is a &lt;strong&gt;topic containing messages&lt;/strong&gt;. A topic is, just like an event stream, a &lt;strong&gt;time-ordered collection of messages&lt;/strong&gt;, with virtually no bounds in the future nor the past — although there is always a first message in a topic and they are generally cleaned up after some time.&lt;/p&gt;

&lt;p&gt;Kafka also provides two APIs implementing the publish/subscribe — a.k.a observable/observer — design pattern. They allow topics to have multiple "inputs" and "outputs":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Producer API&lt;/strong&gt; is used to publish events to topics.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Consumer API&lt;/strong&gt; is used to subscribe to topics and process their messages on the fly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're into handy comparison tables, here you go:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generic term&lt;/th&gt;
&lt;th&gt;Kafka equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Event&lt;/td&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream&lt;/td&gt;
&lt;td&gt;Topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publisher&lt;/td&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscriber&lt;/td&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As far as stream transformation (a.k.a. stream processing) is concerned, there's ways to do that in Kafka. 🎉 There are at least two tools for that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kafka.apache.org/documentation/streams/" rel="noopener noreferrer"&gt;Kafka Streams&lt;/a&gt;: this API provides high-level stream processing capabilities, including mapping, filtering, reducing, windowing, branching, and more. Unfortunately, it is only available for Java and Scala (for now).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.confluent.io/product/ksql/" rel="noopener noreferrer"&gt;KSQL by Confluent&lt;/a&gt;: use plain SQL (and nothing else) to join, aggregate and do windowing on streams in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, stream processing is a rather broad area of interest. We might be able to dig into stream processing in Kafka in future posts. I think sticking to a high-level overview is probably enough for the sake of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should I care?
&lt;/h2&gt;

&lt;p&gt;We've talked about state and requests vs. events and streams, how everything is an event, what streams actually are and how Kafka implements the streaming data paradigm.&lt;/p&gt;

&lt;p&gt;For you, the developer, but also for teams and businesses, &lt;strong&gt;why is any of this important anyway?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  New possibilities
&lt;/h3&gt;

&lt;p&gt;Streaming data is new paradigm that can &lt;strong&gt;open up ideas&lt;/strong&gt; for new systems, concepts and architectures. It enables an &lt;strong&gt;asynchronous, event-driven streaming approach&lt;/strong&gt; that is entirely different from a more classical synchronous, request-driven batch processing approach.&lt;/p&gt;

&lt;p&gt;I also believe it is an exciting opportunity to &lt;strong&gt;learn something new&lt;/strong&gt; and ramp up your skills. When I learnt about Apache Kafka and began to build software with it, I was flabbergasted by how powerful it was — and I loved that it allowed me to build systems communicating via something else than REST APIs and rigid message queues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusable core concepts
&lt;/h3&gt;

&lt;p&gt;Streaming data and event streams are concepts that you can — and may eventually have to — &lt;strong&gt;reapply in other areas of software development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, frontend development is going crazy about event streams at the moment. The whole &lt;a href="https://medium.freecodecamp.org/understanding-redux-the-worlds-easiest-guide-to-beginning-redux-c695f45546f6" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; ecosystem (very fashionable among React developers it seems) is based on events triggering a chain of reactions, eventually updating state.&lt;/p&gt;

&lt;p&gt;You can also reapply streaming data concepts outside of frontend development. For example, the &lt;a href="http://reactivex.io" rel="noopener noreferrer"&gt;ReactiveX API&lt;/a&gt; and its implementations in various languages — &lt;a href="https://github.com/ReactiveX/rxjs" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt; for JavaScript, &lt;a href="https://github.com/ReactiveX/RxPY" rel="noopener noreferrer"&gt;RxPy&lt;/a&gt; for Python, and &lt;a href="http://reactivex.io/languages.html" rel="noopener noreferrer"&gt;more&lt;/a&gt; — bring &lt;strong&gt;streams and events to many popular languages&lt;/strong&gt; in the form of "observable streams". Although Rx is mostly used in web development, you're free to use it for projects in other areas too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiom4k8ctkwtd2heg9hxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiom4k8ctkwtd2heg9hxf.png" width="750" height="253"&gt;&lt;/a&gt;&lt;/p&gt;
The ReactiveX logo. What a cute… eel?



&lt;p&gt;Anyway, put in a quotable form:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As tools and frameworks change ever more rapidly, core concepts are here to stay; streaming data is one such core concept.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Advance your career
&lt;/h3&gt;

&lt;p&gt;Being knowledgeable about distributed streaming platforms is actually super marketable.&lt;/p&gt;

&lt;p&gt;On January 2nd, 2018, LinkedIn published a &lt;a href="https://learning.linkedin.com/blog/top-skills/the-skills-companies-need-most-in-2018--and-the-courses-to-get-t" rel="noopener noreferrer"&gt;report&lt;/a&gt; entitled "The Skills Companies Need Most In 2018". Based on job offers activity, they ranked "Cloud and Distributed Computing" and "Middleware and Integration Software" as respectively top 1 and top 3 most needed skills for companies.&lt;/p&gt;

&lt;p&gt;Guess what? Distributed streaming platforms like Apache Kafka fall in both of these top skill categories.&lt;/p&gt;

&lt;p&gt;Put more casually, &lt;strong&gt;Apache Kafka can help you land a job&lt;/strong&gt; — no less.&lt;/p&gt;

&lt;p&gt;I also think that knowing how to use, deploy and design systems interacting with a distributed streaming platform like Apache Kafka is a great skill to have in your toolbox. You'll be able to &lt;strong&gt;work across multiple projects&lt;/strong&gt; and with a lot of different people, which I've always found downright &lt;strong&gt;thrilling&lt;/strong&gt;. 🔥&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalable systems &lt;em&gt;and teams&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Streaming applications with Kafka are multi-tenant, in the sense that a Kafka topic can have multiple producers and multiple consumers.&lt;/p&gt;

&lt;p&gt;In particular, this means that increasing throughput generally boils down to a) increasing the number of producers and/or b) increasing the number of consumers. &lt;strong&gt;Streaming applications are inherently horizontally scalable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But there's more to it.&lt;/p&gt;

&lt;p&gt;Have you noticed how producers do not have to know anything about who the downstream consumers are? Producers simply push messages to topics, and whoever is interested in them can subscribe to them and do their own processing.&lt;/p&gt;

&lt;p&gt;This means that you can have &lt;strong&gt;loosely coupled teams&lt;/strong&gt; working on separate parts of an application. Their interface is exactly defined by the contents of messages — &lt;strong&gt;the data is the contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fkafka-octopus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fflorimondmanca-personal-website.s3.amazonaws.com%2Fmedia%2Fmarkdownx%2Fkafka-octopus.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
Scaling systems and teams with Kafka Octopus! (I've been waiting for this moment for too long.)



&lt;p&gt;Even better — a new application (say, developed by the BI team) can come and tap into an existing topic &lt;strong&gt;without disrupting the flow of other stream processors&lt;/strong&gt;. Beyond encouraging loose coupling, this property means that &lt;strong&gt;streaming applications allow better scaling of teams&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are many more such benefits of streaming data for human organisations. Here's a last one before we wrap up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reactive businesses
&lt;/h3&gt;

&lt;p&gt;As a company, you want your product or services to be able to &lt;strong&gt;react to changes&lt;/strong&gt; in their environment.&lt;/p&gt;

&lt;p&gt;A typical way of doing so is &lt;strong&gt;batch processing&lt;/strong&gt;. You build up changes in buffering applications, whose task is to prepare batches to be processed by other systems. However, this way of doing is typically slow both in duration (batches being processed over hours) and frequency (a batch being processed twice or three times a day, but no more).&lt;/p&gt;

&lt;p&gt;In contrast, streaming data allows you to process a continuous stream of data. Whatever happens in your system, you can catch it and react to it rapidly. It is then very easy to achieve &lt;strong&gt;minute-level reactivity&lt;/strong&gt;. In the end, streaming data decreases your response time allowing you to wave the killer argument of real-time processing. Boom! 💥&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome to a world of streams and events
&lt;/h2&gt;

&lt;p&gt;As we build systems ever more complex, it turns out that the usual request-based approach is not enough to &lt;strong&gt;harness events&lt;/strong&gt;. Instead, &lt;strong&gt;a stream is a time-ordered collection of events&lt;/strong&gt; and an intuitive and efficient way to build &lt;strong&gt;reactive applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;streaming data paradigm&lt;/strong&gt; is implemented in &lt;strong&gt;Apache Kafka&lt;/strong&gt; through a set of simple yet &lt;strong&gt;powerful abstractions&lt;/strong&gt;, and I strongly believe &lt;strong&gt;you need to become knowledgeable about it&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Updated on December 15, 2018.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I initially planned to write up a final blog post on Apache Kafka's APIs (and perhaps more on cluster deployment), but for various reasons I unfortunately won't have the time to publish them.&lt;/p&gt;

&lt;p&gt;This means that this series is coming to an end! Of course, there is way much more to Kafka than I have covered here! We've only scratched the surface. To continue your journey, I've gathered a few great resources for you to enjoy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@stephane.maarek/how-to-use-apache-kafka-to-transform-a-batch-pipeline-into-a-real-time-one-831b48a6ad85" rel="noopener noreferrer"&gt;How to use Apache Kafka to transform a batch pipline into a real time one&lt;/a&gt;: a thorough example of designing a streaming pipeline, also with a fraud detection example.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@stephane.maarek/introduction-to-schemas-in-apache-kafka-with-the-confluent-schema-registry-3bf55e401321" rel="noopener noreferrer"&gt;Introduction to Schemas in Apache Kafka with the Confluent Schema Registry&lt;/a&gt;: using Avro as a safeguard for your events' formats.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=-GBk0en6dck" rel="noopener noreferrer"&gt;Real Time UI with Apache Kafka Streaming Analytics of Fast Data and Server Push&lt;/a&gt;: example of using Kafka for real-time user interfaces.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=1vLMuWsfMcA" rel="noopener noreferrer"&gt;Lessons Learned From Kafka in Production&lt;/a&gt;: useful advice and rules of thumbs for configuring a live production Kafka cluster.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=I32hmY4diFY" rel="noopener noreferrer"&gt;ETL is Dead, Long Live Streams&lt;/a&gt;: a case for moving from batch to stream processing, and an experience report of building and scaling a streaming platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also highly recommend you O'Reilly's "Apache Kafka: the Definitive Guide". It helped me a lot in the beginning as it contains a lot of useful background and great guidance on how to set up and configure a cluster, how to monitor Kafka as well as more configuration details on consumers and producers.&lt;/p&gt;

&lt;p&gt;Take some time to see how stream processing may be applied to systems you know or you've built, and I'd be happy to hear about the outcome! 🖖💻&lt;/p&gt;

&lt;p&gt;We have now covered some of the core concepts behind streams in Apache Kafka. The next blog post will be more technical as we'll try to make sense of Apache Kafka's APIs and abstractions by looking at their actual implementation.&lt;/p&gt;

&lt;p&gt;Until then, take some time to see how stream processing may be applied to systems you know or you've built, and I'd be happy to hear about the outcome! Stay tuned for more Kafka goodness. 🖖💻&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this post, you can &lt;a href="https://twitter.com/FlorimondManca?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;find me on Twitter&lt;/a&gt; for updates, announcements and news. 🐤&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>stream</category>
      <category>technology</category>
      <category>career</category>
    </item>
  </channel>
</rss>
