<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Timber Staff</title>
    <description>The latest articles on DEV Community by Timber Staff (@timber_staff).</description>
    <link>https://dev.to/timber_staff</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%2F77988%2F1293c447-f998-493b-93ad-6248e5a72375.jpg</url>
      <title>DEV Community: Timber Staff</title>
      <link>https://dev.to/timber_staff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/timber_staff"/>
    <language>en</language>
    <item>
      <title>PromQL For Humans</title>
      <dc:creator>Timber Staff</dc:creator>
      <pubDate>Wed, 24 Oct 2018 20:05:25 +0000</pubDate>
      <link>https://dev.to/timber/promql-for-humans-21n7</link>
      <guid>https://dev.to/timber/promql-for-humans-21n7</guid>
      <description>&lt;p&gt;PromQL is a built in query-language made for Prometheus. Here at Timber &lt;a href="https://timber.io/blog/prometheus-the-good-the-bad-and-the-ugly/" rel="noopener noreferrer"&gt;we've found Prometheus to be awesome&lt;/a&gt;, but PromQL difficult to wrap our heads around. This is our attempt to change that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://files.timber.io/pdfs/PromQL+Cheatsheet.pdf" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiles.timber.io%2Fimages%2FPromQL%2BCheatsheet.jpg" alt="PromQL Cheatsheet" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Basics
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Instant Vectors
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Only Instant Vectors can be graphed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http_requests_total&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/47m73p4320Qm4Cs6AGOC8A/0afdd1172d332697d4a0563d7ef6dce5/http_requests_total.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/47m73p4320Qm4Cs6AGOC8A/0afdd1172d332697d4a0563d7ef6dce5/http_requests_total.png" alt="http requests total"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives us all the http requests, but we've got 2 issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are too many data points to decipher what's going on.&lt;/li&gt;
&lt;li&gt;You'll notice that &lt;code&gt;http_requests_total&lt;/code&gt; only goes up, because it's a &lt;a href="https://prometheus.io/docs/concepts/metric_types/#counter" rel="noopener noreferrer"&gt;counter&lt;/a&gt;. These are common in Prometheus, but not useful to graph.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll show you how to approach both.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's easy to filter by label.
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;http_requests_total{job="prometheus", code="200"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/1YYMQf9uS4Oi0Q2KeiSWc6/aa1493fe8cef2e23d4430b84c9f9feb4/filter-by-label.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/1YYMQf9uS4Oi0Q2KeiSWc6/aa1493fe8cef2e23d4430b84c9f9feb4/filter-by-label.png" alt="filter-by-label"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  You can check a substring using regex matching.
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;http_requests_total{status_code=~"2.*"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/49euNSlsFGU4kA0qa44oEG/0538688f02c42cafd31054922ffd8b10/substring.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/49euNSlsFGU4kA0qa44oEG/0538688f02c42cafd31054922ffd8b10/substring.png" alt="substring"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in learning more, &lt;a href="https://docs.python.org/3/library/re.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; are the docs on Regex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Range Vectors
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Contain data going back in time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recall: Only Instant Vectors can be graphed. You'll soon be able to see how to visualize Range Vectors using functions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http_requests_total[5m]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also use (s, m, h, d, w, y) to represent (seconds, minutes, hours, ...) respectively.&lt;/p&gt;

&lt;h1&gt;
  
  
  Important functions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  For Range Vectors
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;You'll notice that we're able to graph all these functions. Since only Instant Vectors can be graphed, they take a Range Vector as a parameter and return a Instant Vector.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Increase of &lt;code&gt;http_requests_total&lt;/code&gt; averaged over the last 5 minutes.
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rate(http_requests_total[5m])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/1oQ9xOzINe4mIqIwkIwcu8/a421fa29abb27648e967a38e8425ef65/rate.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/1oQ9xOzINe4mIqIwkIwcu8/a421fa29abb27648e967a38e8425ef65/rate.png" alt="rate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Irate
&lt;/h3&gt;

&lt;p&gt;Looks at the 2 most recent samples (up to 5 minutes in the past), rather than averaging like &lt;code&gt;rate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;irate(http_requests_total[5m])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/3MCqVRoOPCOuKG6MOsqYcu/0aac574e965b4d960bf83ad8477ce5f9/irate.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/3MCqVRoOPCOuKG6MOsqYcu/0aac574e965b4d960bf83ad8477ce5f9/irate.png" alt="irate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's best to use &lt;code&gt;rate&lt;/code&gt; when alerting, because it creates a smooth graph since the data is averaged over a period of time. &lt;em&gt;Spikey graphs can cause alert overload, fatigue, and bad times for all due to repeatedly triggering thresholds.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Requests in the last hour.
&lt;/h3&gt;

&lt;p&gt;This is equal to the &lt;code&gt;rate&lt;/code&gt; * # of seconds&lt;/p&gt;

&lt;p&gt;&lt;code&gt;increase(http_requests_total[1h])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/4UqNZH5G80WaowamI884MY/9e991a116fd1606b42c9978b72edb26f/increase.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/4UqNZH5G80WaowamI884MY/9e991a116fd1606b42c9978b72edb26f/increase.png" alt="increase"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are a small fraction of the functions, just what we found most popular. You can find the rest &lt;a href="https://prometheus.io/docs/prometheus/latest/querying/functions/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  For Instant Vectors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Broken by Status Code
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sum(rate(http_requests_total[5m]))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You'll notice that &lt;code&gt;rate(http_requests_total[5m])&lt;/code&gt; above provides a large amount of data. You can filter that data using your labels, but you can also look at your system as a whole using &lt;code&gt;sum&lt;/code&gt; (or do both).&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;avg&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, and &lt;code&gt;quantile&lt;/code&gt; similarly.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/6t6cQFBJ7i6yqOU2CCsQwW/4216d8b6efef36ec0b49ae601a65e545/sum-rate.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/6t6cQFBJ7i6yqOU2CCsQwW/4216d8b6efef36ec0b49ae601a65e545/sum-rate.png" alt="sum-rate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This query tells you how many total HTTP requests there are, but isn't directly useful in deciphering issues in your system. I'll show you some functions that allow you to gain insight into your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sum by Status Code
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sum by (status_code) (rate(http_requests_total[5m]))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;without&lt;/code&gt; rather than &lt;code&gt;by&lt;/code&gt; to sum on everything not passed as a parameter to without.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/MyLjzpWl4kcm0gwGSSaOA/4473db77e505c9322f68b533c2a5cc86/sum-by-rate.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/MyLjzpWl4kcm0gwGSSaOA/4473db77e505c9322f68b533c2a5cc86/sum-by-rate.png" alt="sum-by-rate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can see the difference between each status code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offset
&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;offset&lt;/code&gt; to change the time for Instant and Range Vectors. This can be helpful for comparing current usage to past usage when determining the conditions of an alert.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sum(rate(http_requests_total[5m] offset 5m))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Remember to put &lt;code&gt;offset&lt;/code&gt; directly after the selector.&lt;/p&gt;

&lt;h1&gt;
  
  
  Operators
&lt;/h1&gt;

&lt;p&gt;Operators can be used between scalars, vectors, or a mix of the two. Operations between vectors expect to find matching elements for each side (also known as one-to-one matching), unless otherwise specified.&lt;/p&gt;

&lt;p&gt;There are Arithmetic (+, -, *, /, %, ^), Comparison (==, !=, &amp;gt;, &amp;lt;, &amp;gt;=, &amp;lt;=) and Logical (and, or, unless) operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector Matching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One-to-One&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vectors are equal i.f.f. the labels are equal.&lt;/p&gt;

&lt;h3&gt;
  
  
  API 5xxs are 10% of HTTP Requests
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rate(http_requests_total{status_code=~"5.*"}[5m]) &amp;gt; .1 * rate(http_requests_total[5m])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/22x7QobKuMEIU88AYkea0Y/ded5098f90c33fb9e9cf7b978580de3f/api5xx.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/22x7QobKuMEIU88AYkea0Y/ded5098f90c33fb9e9cf7b978580de3f/api5xx.png" alt="api5xx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're looking to graph whenever more than 10% of an instance's HTTP requests are errors. Before comparing rates, PromQL first checks to make sure that the vector's labels are equal.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;on&lt;/code&gt; to compare using certain labels or &lt;code&gt;ignoring&lt;/code&gt; to compare on all labels except.&lt;/p&gt;

&lt;h2&gt;
  
  
  Many-to-One
&lt;/h2&gt;

&lt;p&gt;It's possible to use comparison and arithmetic operations where an element on one side can be matched with many elements on the other side. &lt;em&gt;You must explicitly tell Prometheus what to do with the extra dimensions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;group_left&lt;/code&gt; if the left side has a higher cardinality, else use &lt;code&gt;group_right&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Examples
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: We've hidden some of the information in the pictures using the &lt;code&gt;Legend Format&lt;/code&gt; for privacy reasons.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Usage by Instance
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;100 * (1 - avg by(instance)(irate(node_cpu{mode='idle'}[5m])))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Average CPU Usage per instance for a 5 minute window.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/3rbFceQUaQ0w2O2sogi4w6/bbf45cfd8a92ac6a7fcfe24eca268b6a/cpu.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/3rbFceQUaQ0w2O2sogi4w6/bbf45cfd8a92ac6a7fcfe24eca268b6a/cpu.png" alt="cpu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Usage
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;node_memory_Active / on (instance) node_memory_MemTotal&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Percentage of memory being used by instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/5wKZrzMiHu6y80au0AOQ2G/3a7419d79587b9e8690787b1099a66d0/memory.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/5wKZrzMiHu6y80au0AOQ2G/3a7419d79587b9e8690787b1099a66d0/memory.png" alt="memory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disk Space
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;node_filesystem_avail{fstype!~"tmpfs|fuse.lxcfs|squashfs"} / node_filesystem_size{fstype!~"tmpfs|fuse.lxcfs|squashfs"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Percentage of disk space being used by instance. We're looking for the available space, ignoring instances that have &lt;code&gt;tmpfs&lt;/code&gt;, &lt;code&gt;fuse.lxcfs&lt;/code&gt;, or &lt;code&gt;squashfs&lt;/code&gt; in their &lt;code&gt;fstype&lt;/code&gt; and dividing that by their total size.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/66tln8JKuWuyQ2CwA4gaCY/40de7b35900834ac2de4c47757db8d63/disk.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/66tln8JKuWuyQ2CwA4gaCY/40de7b35900834ac2de4c47757db8d63/disk.png" alt="disk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Error Rates as a % of Traffic
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rate(http_requests_total{status_code=~"5.*"}[5m]) / rate(http_requests_total[5m])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/11WaHWtONckUeYcqY4YYyc/ba85a9ba85c37d2b74d81ff60d6f4dff/http_error_rates.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/11WaHWtONckUeYcqY4YYyc/ba85a9ba85c37d2b74d81ff60d6f4dff/http_error_rates.png" alt="http error rates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Alerts Firing in the last 24 hours
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sum(sort_desc(sum_over_time(ALERTS{alertstate="firing"}[24h]))) by (alertname)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/bk7qkd88FykSwoI8gIeoa/1b865a815087b373da64d13483fc3d23/alerts_firing.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/bk7qkd88FykSwoI8gIeoa/1b865a815087b373da64d13483fc3d23/alerts_firing.png" alt="alerts firing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find more useful examples &lt;a href="https://github.com/infinityworks/prometheus-example-queries" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  3 Pillars of Observability
&lt;/h1&gt;

&lt;p&gt;It's important to understand where metrics fit in when it comes to observing your application. I recommend you take a look at the &lt;a href="https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html" rel="noopener noreferrer"&gt;3 pillars of observability&lt;/a&gt; principle.  Metrics are an important part of your observability stack, but &lt;em&gt;logs&lt;/em&gt; and tracing are equally so.&lt;/p&gt;

&lt;p&gt;We're a cloud-based logging company at Timber that seamlessly augments your logs with context. We've got &lt;a href="https://timber.io/" rel="noopener noreferrer"&gt;a great product&lt;/a&gt; built, and you can check it out for free!&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%2Fg9sazavymzet50mub5a7.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%2Fg9sazavymzet50mub5a7.png" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>promql</category>
      <category>prometheus</category>
    </item>
    <item>
      <title>A Gentle Introduction to GraphQL with Elixir and Phoenix</title>
      <dc:creator>Timber Staff</dc:creator>
      <pubDate>Wed, 24 Oct 2018 20:04:49 +0000</pubDate>
      <link>https://dev.to/timber/a-gentle-introduction-to-graphql-with-elixir-and-phoenix-736</link>
      <guid>https://dev.to/timber/a-gentle-introduction-to-graphql-with-elixir-and-phoenix-736</guid>
      <description>&lt;h2&gt;
  
  
  Getting a working GraphQL application!
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Latest Versions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Elixir: v1.6.5&lt;/li&gt;
&lt;li&gt;Hex: v0.17.7&lt;/li&gt;
&lt;li&gt;Phoenix: v1.3.2&lt;/li&gt;
&lt;li&gt;Ecto: v2.2.10 (phoenix_ecto version is v3.2)&lt;/li&gt;
&lt;li&gt;Absinthe: v1.4&lt;/li&gt;
&lt;li&gt;Absinthe Plug: v1.4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial was built on a Mac OS X 10.11.6 system.&lt;/p&gt;

&lt;h4&gt;
  
  
  Assumptions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; You understand a minimum of Elixir syntax and concepts&lt;/li&gt;
&lt;li&gt; You already have a development database ready to go&lt;/li&gt;
&lt;li&gt; You have all of the pre-requisite software installed. Pre-reqs are:

&lt;ul&gt;
&lt;li&gt;Postgresql with an appropriate development username/password (&lt;a href="https://wiki.postgresql.org/wiki/Detailed_installation_guides"&gt;Installation Guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Elixir (&lt;a href="https://elixir-lang.org/install.html"&gt;Installation Guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Phoenix (&lt;a href="https://hexdocs.pm/phoenix/installation.html#content"&gt;Installation Guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I'll be using &lt;a href="https://code.visualstudio.com/Download"&gt;Visual Studio Code&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  About this guide
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This is a guest post bought to you by your friends @ &lt;a href="https://timber.io/"&gt;Timber&lt;/a&gt;. Reach out &lt;a href="https://twitter.com/timberdotio"&gt;on Twitter&lt;/a&gt; if you're interested in writing for us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Throughout this guide, you'll see a lot of useful stuff, whether it's descriptions, code, or shell commands! Any time you see some code like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ do the thing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These blocks represents shell commands being run (specifically, anything with a &lt;code&gt;$&lt;/code&gt; in front of it). Any additional lines will be output from the command run. Sometimes &lt;code&gt;...&lt;/code&gt; will be used to indicate long blocks of text cut out in interest of brevity.&lt;/p&gt;

&lt;p&gt;IEx (Elixir's Interactive REPL) has commands will be represented like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; "do the thing"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And any additional lines will be output from that operation. Don't worry about the number inside of the parantheses. These just indicate what command number your IEx shell is currently on and these may not match up.&lt;/p&gt;

&lt;p&gt;Code will be represented in code blocks, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"brandon"&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upcase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best way to get the most out of this tutorial is to follow along as you build the same project as us (or something equivalent). The code will be available on Github with checkpoint tags at each step allowing you to double-check your code against the finished product per each checkpoint!&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving on to the application
&lt;/h2&gt;

&lt;p&gt;We'll start off by actually talking about what problem this project is designed to solve. It's always good to think about what you're building before you start thinking about what you're building with; it prevents some chicken-and-egg scenarios where you have a great technology that you're using and the project you're trying to build with it is totally incompatible.&lt;/p&gt;

&lt;p&gt;The project we're building is going to be an application that will store/retrieve Event Logs. You could use this for something like tracking requests you're making, or tracking audit events in a log, or...well, anything that would require storing some arbitrary events with types, messages, and payloads. We'll start off by actually creating our project and getting everything set up before we start talking more about the design.&lt;/p&gt;

&lt;p&gt;Next, our tech choice: Elixir and Phoenix are absolutely fantastic for building extremely high performance, low maintenance systems, and GraphQL's ability to fetch huge sets of data in varied ways makes Elixir a particularly great symbiotic fit!&lt;/p&gt;

&lt;p&gt;Our project will be called "Greenfy" short for "Green Faerie" (yes, a silly Absinthe reference), but slightly more startup-sounding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a new project
&lt;/h3&gt;

&lt;p&gt;This is what happened:&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;mix phx.new greenfy

    Fetch and &lt;span class="nb"&gt;install &lt;/span&gt;dependencies? &lt;span class="o"&gt;[&lt;/span&gt;Yn] y
    ...
    We are all &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Go into your application by running:

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

    Then configure your database &lt;span class="k"&gt;in &lt;/span&gt;config/dev.exs and run:

    &lt;span class="nv"&gt;$ &lt;/span&gt;mix ecto.create

    Start your Phoenix app with:

    &lt;span class="nv"&gt;$ &lt;/span&gt;mix phx.server

    You can also run your app inside IEx &lt;span class="o"&gt;(&lt;/span&gt;Interactive Elixir&lt;span class="o"&gt;)&lt;/span&gt; as:

    &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run our tests, verify green.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up our app
&lt;/h3&gt;

&lt;p&gt;Next, we'll want to follow the instructions given to us by Phoenix upon creating a new project, so we'll need to do the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd greenfy
$ mix ecto.create
The database for Greenfy.Repo has been created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should already have your database set up with a user account that can create and modify databases as necessary, so when you run the &lt;code&gt;mix ecto.create&lt;/code&gt; command, Ecto will helpfully create the development database for us to run our migrations on later!&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding in Absinthe
&lt;/h3&gt;

&lt;p&gt;We know we're going to be building a GraphQL project, so we're going to add Absinthe nice and early in the process. We'll need to add both libraries for &lt;code&gt;absinthe&lt;/code&gt; and &lt;code&gt;absinthe_plug&lt;/code&gt; (we'll need &lt;code&gt;absinthe_plug&lt;/code&gt; since we're using Phoenix to build out our application).&lt;/p&gt;

&lt;p&gt;Open up &lt;code&gt;mix.exs&lt;/code&gt; and add the following line to the &lt;code&gt;defp deps do&lt;/code&gt; function block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:absinthe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.4"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:absinthe_plug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.4"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you add a comma at the end of what was previously the last line in that list of tuples or you'll get a syntax error! At the top, in the block for the &lt;code&gt;application&lt;/code&gt; function, you'll want to add in support for Absinthe Plug via adding one more atom into the extra_applications list, &lt;code&gt;:absinthe_plug&lt;/code&gt;. The resulting function body should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;mod:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]},&lt;/span&gt;
      &lt;span class="ss"&gt;extra_applications:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:runtime_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:absinthe_plug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;    &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="no"&gt;Generated&lt;/span&gt; &lt;span class="n"&gt;greenfy&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may be wondering why we're adding both &lt;code&gt;absinthe&lt;/code&gt; and &lt;code&gt;absinthe_plug&lt;/code&gt;. The reason for this is that first of all, the &lt;code&gt;absinthe&lt;/code&gt; dependency handles &lt;em&gt;most&lt;/em&gt; of the scenarios we need handled for a smooth integration between GraphQL and Elixir,&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our first GraphQL query
&lt;/h3&gt;

&lt;p&gt;So, you're probably coming into this tutorial already having an inkling of what GraphQL is and what it does and what it provides for both the developer and the end-user. A pitfall I've run into repeatedly over the course of my career is that most REST APIs over time become less RESTful. There are more and more exceptions you need to build into your application, and some of your endpoints become too bloated and they're difficult to modify out or...&lt;/p&gt;

&lt;p&gt;Well, lots of things happen. Especially if you have a mobile or client application that is reliant on your data! The shape of what you might fetch for a desktop client is likely going to look radically different from what you would fetch for a mobile client, especially on a slower connection.&lt;/p&gt;

&lt;p&gt;GraphQL recognizes two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The shape of your data will change&lt;/li&gt;
&lt;li&gt; The shape of your data needs to be different for each client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on that, GraphQL implements more of a client-focused method of getting data out of a system. The client will tell the server what data it needs, and more importantly, HOW it needs that data to be shaped! This allows you to iterate a little faster, to build better filters and functionality, and easily add support for more data in your system without having to build/version a whole brand new endpoint that you will also need to support over time.&lt;/p&gt;

&lt;p&gt;Another awesome feature is that GraphQL supports querying for schema information, which means your users can do more to figure out the shape of the data and what they can do with it, which is incredibly helpful for both the client AND the server!&lt;/p&gt;

&lt;p&gt;GraphQL is not perfect, by any means. It's pretty complicated and you'll likely run into a lot of people with lots of REST experience not understanding why a particular piece of a GraphQL query works the way it does. You'll need to train your users to be able to use this new API effectively, but the end result is allowing the client to get whatever they want, however they want it!&lt;/p&gt;

&lt;p&gt;With that past us, let's move on to talk more about the actual implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Designing the shape of our data
&lt;/h4&gt;

&lt;p&gt;Our application is a simple event logging platform, so we're just going to have a very simple table structure (to start).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events
___
id
event_type
message
payload
inserted_at
updated_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;inserted_at&lt;/code&gt;, and &lt;code&gt;updated_at&lt;/code&gt; will all be included as default columns for any Ecto tables we create, so we don't need to worry about how we're going to define those.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;event_type&lt;/code&gt;, &lt;code&gt;payload&lt;/code&gt;, and &lt;code&gt;message&lt;/code&gt; columns, we're going to use those to track what types of events (unsurprisingly) each event is and message will store a condensed version of the payload. Separately, we have another column called &lt;code&gt;payload&lt;/code&gt;, whose responsibility is to store the full payload when necessary. This allows the &lt;code&gt;message&lt;/code&gt; column to stay smaller and easier to search.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting our database tables running in Ecto
&lt;/h4&gt;

&lt;p&gt;Let's get started by building out this table using Phoenix's built-in generators:&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;mix phx.gen.context Log Events events event_type:string message:string payload:text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;creating lib/greenfy/log/events.ex&lt;/li&gt;
&lt;li&gt;creating priv/repo/migrations/20180614191428_create_events.exs&lt;/li&gt;
&lt;li&gt;creating lib/greenfy/log/log.ex&lt;/li&gt;
&lt;li&gt;injecting lib/greenfy/log/log.ex&lt;/li&gt;
&lt;li&gt;creating test/greenfy/log/log_test.exs&lt;/li&gt;
&lt;li&gt;injecting test/greenfy/log/log_test.exs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to update your repository by running migrations:&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;mix ecto.migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's run the migrate command:&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;mix ecto.migrate
&lt;span class="o"&gt;[&lt;/span&gt;info] &lt;span class="o"&gt;==&lt;/span&gt; Running Greenfy.Repo.Migrations.CreateEvents.change/0 forward
&lt;span class="o"&gt;[&lt;/span&gt;info] create table events
&lt;span class="o"&gt;[&lt;/span&gt;info] &lt;span class="o"&gt;==&lt;/span&gt; Migrated &lt;span class="k"&gt;in &lt;/span&gt;0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is actually enough for us to start building out the GraphQL portion of our application!&lt;/p&gt;

&lt;h4&gt;
  
  
  Building support for our first query in GraphQL
&lt;/h4&gt;

&lt;p&gt;Let's take a look at what a sample query for our &lt;code&gt;Log.Events&lt;/code&gt; schema might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    events {
        id
        event_type
        message
        payload
        inserted_at
        updated_at
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this work, we'll need to define both the Types and the Schemas for GraphQL to understand how to turn data in the database, and then data in Elixir data structures, into GraphQL data structures. We'll start off by defining the types. Create a new file, under &lt;code&gt;lib/greenfy_web/schema&lt;/code&gt;, called &lt;code&gt;data_types.ex&lt;/code&gt; (you'll need to create the &lt;code&gt;schema&lt;/code&gt; directory), and we'll start defining our first object type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataTypes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Notation&lt;/span&gt;

  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:event&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll probably notice that &lt;code&gt;:inserted_at&lt;/code&gt; and &lt;code&gt;:updated_at&lt;/code&gt; are not being included in our data types notation yet! The reason for this is that those are not easily represented by the standard built-in types for Absinthe, so we'll need to define some fancy &lt;code&gt;scalar&lt;/code&gt; types later! (Don't worry, we'll describe this all in great detail later!)&lt;/p&gt;

&lt;h4&gt;
  
  
  Sidebar: GraphQL Definitions
&lt;/h4&gt;

&lt;p&gt;We're using A LOT of terms here pretty liberally when talking about GraphQL but not doing a lot to explain what they actually are, so let's take a really quick sidebar talking about some of the terms we've discussed so far (and the ones we'll be doing later).&lt;/p&gt;

&lt;p&gt;First off, we have &lt;strong&gt;Types&lt;/strong&gt;. Types are exactly what they sound like: definitions of the various data types we'll be defining for our GraphQL application. A good example of this is the object definition above: An &lt;strong&gt;event&lt;/strong&gt; is a GraphQL &lt;strong&gt;object&lt;/strong&gt; type, with four &lt;strong&gt;fields&lt;/strong&gt; defined on it so far (we'll get to fields next).&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Field&lt;/strong&gt; is essentially a bit of queryable information. An object, like the data type we defined previously, stores a collection of different fields together. Each field should define, at a minimum, its name (or key) and its type.&lt;/p&gt;

&lt;p&gt;Next, we'll be working with &lt;strong&gt;Schemas&lt;/strong&gt;. If &lt;strong&gt;Types&lt;/strong&gt; define what data is queryable in our GraphQL API and the shape of that data, the &lt;strong&gt;Schema&lt;/strong&gt; defines how we actually get that data OUT of our GraphQL API.&lt;/p&gt;

&lt;p&gt;Later on, you'll see something called &lt;strong&gt;Resolvers&lt;/strong&gt;. If the &lt;strong&gt;Schema&lt;/strong&gt; tells the client how to query the data out of our application, the &lt;strong&gt;Resolver&lt;/strong&gt; tells the server how to react to that query and how to interpret those GraphQL queries.&lt;/p&gt;

&lt;p&gt;Finally, this will be something we'll use much later, but you'll hear it mentioned before that point: &lt;strong&gt;Mutations&lt;/strong&gt;! Mutations are how the data in our GraphQL application is modified! Instead of your &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;PATCH&lt;/code&gt; endpoints in your REST API, you just define &lt;strong&gt;Mutations&lt;/strong&gt; instead.&lt;/p&gt;

&lt;h4&gt;
  
  
  Back to our implementation
&lt;/h4&gt;

&lt;p&gt;Next, we'll need to create our &lt;code&gt;Schema&lt;/code&gt;. Create &lt;code&gt;lib/greenfy_web/schema.ex&lt;/code&gt; and give it the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;import_types&lt;/span&gt; &lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataTypes&lt;/span&gt;

  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nv"&gt;@desc&lt;/span&gt; &lt;span class="s2"&gt;"Get a list of events"&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_resolution&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_events&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the provided &lt;code&gt;Absinthe.Schema&lt;/code&gt; macro to build out our Schema body. Next, we define our query body, which starts off with a &lt;code&gt;@desc&lt;/code&gt; statement, which sets a module variable called &lt;code&gt;desc&lt;/code&gt; (unsurprisingly) which describes the general use for this query. Think of it as a bit of documentation for the client.&lt;/p&gt;

&lt;p&gt;Next, in our query, we have a field. Again, our field is queryable data, so we say that our "query" has a key in it called &lt;code&gt;events&lt;/code&gt;. We're also telling Absinthe that we should expect &lt;code&gt;events&lt;/code&gt; to return a &lt;strong&gt;list of events&lt;/strong&gt;, so we use the function &lt;code&gt;list_of(:event)&lt;/code&gt; (note the singular version of &lt;code&gt;event&lt;/code&gt; and that it is an atom, here). This tells Absinthe that when someone queries &lt;code&gt;events&lt;/code&gt;, we should be fetching a list of the Data Type &lt;code&gt;Event&lt;/code&gt; that we defined in our Data Types file earlier.&lt;/p&gt;

&lt;p&gt;Finally, we have a &lt;strong&gt;Resolver&lt;/strong&gt;, which tells Absinthe that when a client queries for a list of events, this function's return statement is what data should be passed back to the client (after some transformation, of course). A &lt;code&gt;resolve&lt;/code&gt;statement takes in three arguments; the &lt;code&gt;parent&lt;/code&gt;, the &lt;code&gt;args&lt;/code&gt; (the arguments to our query, so whatever we might be looking for or filtering on), and the &lt;code&gt;resolution&lt;/code&gt;, which tells us what to do when we've fetched all of the data and what to do with that data at the end! We'll build on this later by moving our resolvers off into their own code, but for right now we're going to stick with a simple implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  One final step before we can start testing
&lt;/h4&gt;

&lt;p&gt;Before we get too deep, though, let's modify our router so we can start testing out our application and refactoring as appropriate. Open up &lt;code&gt;lib/greenfy_web/router.ex&lt;/code&gt;, delete the following block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:browser&lt;/span&gt; &lt;span class="c1"&gt;# Use the default browser stack&lt;/span&gt;

    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PageController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncomment out the &lt;code&gt;/api&lt;/code&gt; scope section, and remove the mention of the Application module, "Greenfy" from the scope block. Then, we'll want to add the GraphiQL route to give us a playground to test out our application. The scope statement should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/api"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;

    &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="s2"&gt;"/graphiql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;GraphiQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;schema:&lt;/span&gt; &lt;span class="no"&gt;Greenfy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  And finally, testing it out
&lt;/h3&gt;

&lt;p&gt;In your terminal, start up your server by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="no"&gt;S&lt;/span&gt; &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then open up your browser window and point it at &lt;code&gt;http://localhost:4000/api/graphiql&lt;/code&gt;. You should see a window that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/7GvswSUCVaogsIi0AYoKmw/1a97c4f992c3823f323804881b03bf8b/elixir-graphql-1__1_.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/7GvswSUCVaogsIi0AYoKmw/1a97c4f992c3823f323804881b03bf8b/elixir-graphql-1__1_.png" alt="elixir-graphql-1 (1)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see in this screenshot that we have our initial query running, selecting the primary key, eventType (note the difference from &lt;code&gt;event_type&lt;/code&gt;; GraphQL expects JavaScript-style Camel-case whereas Elixir by default expects snake_case style. Absinthe handles this conversion for us!) We're not selecting the &lt;code&gt;inserted_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; columns yet; we still have time to get to that later!&lt;/p&gt;

&lt;h3&gt;
  
  
  Before You Go
&lt;/h3&gt;

&lt;p&gt;Just wanted to catch you before you finished reading to let you know we're a cloud-based logging company that's looking to make debugging easier by seamlessly augmenting logs with context. We've got a &lt;a href="https://timber.io/"&gt;great product&lt;/a&gt; built and you can check it out for free! &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/h6vh38q7qvzk/5BUP5dDcrKae4yyaoy8ocE/ba33ae45edec6325109f05a44407a2e2/footer.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/h6vh38q7qvzk/5BUP5dDcrKae4yyaoy8ocE/ba33ae45edec6325109f05a44407a2e2/footer.png" alt="footer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;So now we're at a point where we have a base GraphQL API and support for VERY limited operations to get the data out! We understand a bit more about GraphQL, the terminology used, and how it all fits together, so where do we go from here?&lt;/p&gt;

&lt;p&gt;Easy: we'll make this application more robust! For one thing, we can't verify this works super well unless we can also get data into it. In addition, testing is already hard, so how on earth are we going to test something so completely malleable?&lt;/p&gt;

&lt;p&gt;Stay tuned! We have a lot more to talk about, a lot more to understand, and hopefully we can eventually land at being experts at implementing GraphQL APIs in Elixir.&lt;/p&gt;

&lt;p&gt;Following along? You can check your work against the tutorial repo at &lt;a href="https://github.com/richeyb/greenfy-example"&gt;Github&lt;/a&gt;. Tag/Release v0.1.0 is the complete version for this phase of the tutorial.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Decorators in Python: What you need to know</title>
      <dc:creator>Timber Staff</dc:creator>
      <pubDate>Fri, 10 Aug 2018 20:07:54 +0000</pubDate>
      <link>https://dev.to/timber/decorators-in-python-what-you-need-to-know-2iaf</link>
      <guid>https://dev.to/timber/decorators-in-python-what-you-need-to-know-2iaf</guid>
      <description>&lt;p&gt;Python decorators are a powerful concept that allow you to "wrap" a function with another function.&lt;/p&gt;

&lt;p&gt;The idea of a decorator is to abstract away something that you want a function or class to do, besides its normal responsibility. This can be helpful for many reasons such as code reuse, and sticking to &lt;a href="https://blog.codinghorror.com/curlys-law-do-one-thing/"&gt;curlys law&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;By learning how to write your own decorators, you can significantly improve readability of your own code. They can change how the function behaves, without needing to actually change the code (such as adding logging lines). They are a fairly common tool in python, familiar to those who use frameworks such as flask or click, but many only know how to use them, not how to write their own.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a guest-post brought to you by your friends @ Timber. If you're interested in writing for us, feel free to reach out &lt;a href="https://twitter.com/timberdotio"&gt;on Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How it works
&lt;/h1&gt;

&lt;p&gt;First off, let's show an example of a decorator in python. The following is a very basic example of what a decorator would like like if you were using it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;my_decorator&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;When you define a function in python, that function becomes an object.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The function &lt;code&gt;hello&lt;/code&gt; above is a function object. The &lt;code&gt;@my_decorator&lt;/code&gt; is actually a function that has the ability to use the &lt;code&gt;hello&lt;/code&gt; object, and return a different object to the interpreter. The object that the decorator returns, is what becomes known as &lt;code&gt;hello&lt;/code&gt;. Essentially, it's the same thing as if you were going to write your own normal function, such as: &lt;code&gt;hello = decorate(hello)&lt;/code&gt;. Decorate is passed the function -- which it can use however it wants -- then returns another object. The decorator has the ability to swallow the function, or return something that is not a function, if it wanted. &lt;/p&gt;

&lt;h1&gt;
  
  
  Writing your own decorator
&lt;/h1&gt;

&lt;p&gt;As mentioned, a decorator is simply a function that is passed a function, and returns an object. So, to start writing a decorator, we just need to define a function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any function can be used as a decorator. In this example the decorator is passed a function, and returns a different object. It simply swallows the function it is given entirely, and always returns 5.&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;@&lt;/span&gt;&lt;span class="n"&gt;my_decorator&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'int'&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;callable&lt;/span&gt;
&lt;span class="s"&gt;'int'&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;callable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because our decorator is returning an int, and not a &lt;code&gt;callable&lt;/code&gt;, it can not be called as a function. Remember, the decorator's return value &lt;em&gt;replaces&lt;/em&gt; &lt;code&gt;hello&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;hello&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most cases, we want the object our decorator returns, to actually mimic the function we decorated. &lt;em&gt;This means that the object the decorator returns, needs to be a function itself&lt;/em&gt;. For example, let's say we wanted to simply print every time the function was called, we could write a function that prints that information, then calls the function. But that function would need to be returned by the decorator. This usually leads to function nesting such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mydecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# f is the function passed to us from python
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_f_as_called&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; was called.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_f_as_called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we have a nested function definition, and the decorator function is returning that function. This way, the function &lt;code&gt;hello&lt;/code&gt; can still be called like a standard function, the caller does not need to know that it is decorated. We can now define &lt;code&gt;hello&lt;/code&gt; as the following:&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;@&lt;/span&gt;&lt;span class="n"&gt;mydecorator&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get the following output:&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;hello&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;function&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f27738d7510&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;called&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: the number inside the &lt;code&gt;&amp;lt;function hello at 0x7f27738d7510&amp;gt;&lt;/code&gt; reference will be different for everyone, it represents the memory address)&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping functions correctly
&lt;/h1&gt;

&lt;p&gt;A function can be decorated many times, if desired. In this case, the decorators cause a chain effect. Essentially, the top decorator is passed the object from the former, and so on and so forth. For example, if we have 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="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;c&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interpreter is essentially doing &lt;code&gt;hello = a(b(c(hello)))&lt;/code&gt; and all of the decorators would wrap each other. You can test this out yourself by using our existing decorator, and using it twice.&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;@&lt;/span&gt;&lt;span class="n"&gt;mydecorator&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;mydecorator&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&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;hello&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;function&lt;/span&gt; &lt;span class="n"&gt;mydec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;locals&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;a&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f277383d378&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;called&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;function&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f2772f78ae8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;called&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice that the first decorator, wrapped the second, and printed it separately. &lt;/p&gt;

&lt;p&gt;One interesting thing you might have noticed here, is that first line printed said &lt;code&gt;&amp;lt;function mydec.&amp;lt;locals&amp;gt;.a at 0x7f277383d378&amp;gt;&lt;/code&gt; instead of what the second line printed, which is what you would expect: &lt;code&gt;&amp;lt;function hello at 0x7f2772f78ae8&amp;gt;&lt;/code&gt;. The is because the object returned by the decorator is a new function, not called &lt;code&gt;hello&lt;/code&gt;. This is fine for our trivial example, but can often break tests and things that might be trying to introspect the function attributes. If the idea is for a decorator to act like the function it decorates, it needs to also mimic that function. Luckily, there is a python standard library decorator called &lt;code&gt;wraps&lt;/code&gt; for that in functools module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mydecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# we tell wraps that the function we are wrapping is f
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_f_as_called&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; was called.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_f_as_called&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;mydecorator&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;mydecorator&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&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;hello&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;function&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f27737c7950&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;called&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;function&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f27737c7f28&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;called&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, our new function appears just like the one its wrapping/decorating. However, we are still reliant on the fact that it returns nothing, and accepts no input. If we wanted to make that more general, we would need to pass in the arguments and return the same value. We can modify our function to 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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mydecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# wraps is a decorator that tells our function to act like f
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_f_as_called&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; was called with arguments=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and kwargs=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kwargs&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="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; return value &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_f_as_called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are printing everytime the function is called, including all the inputs the function receives, and what it returns. Now you can simply decorate any existing function and get debug logging on all its inputs and outputs without needed to manually write the logging code. &lt;/p&gt;

&lt;h1&gt;
  
  
  Adding variables to the decorator
&lt;/h1&gt;

&lt;p&gt;If you were using the decorator for any code that you wanted to ship, and not just for yourself locally, you might want to replace all the &lt;code&gt;print&lt;/code&gt; statements with logging statements. In which case, you would need to define a log level. It might be safe to assume you always want the debug log level, but what that also might depend on the function. We can provide variables to the decorator itself, to define how it should behave. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'info'&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code would allow us to specify that this specific function should log at the info level instead of the debug level. This is achieved in python by writing a function, that returns a decorator. Yes, a decorator is also a function. So this is essentially saying &lt;code&gt;hello = debug('info')(hello)&lt;/code&gt;. That double parenthesis might look funky, but basically, debug is function, that returns a function. Adding this to our existing decorator, we would need to nest one more time, now making our code look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&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;mydecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_f_as_called&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; was called with arguments=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and kwargs=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kwargs&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="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; return value &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_f_as_called&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mydecorator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes above turn &lt;code&gt;debug&lt;/code&gt; into a function, which returns a decorator which uses the correct logging level. This is starting to get a bit ugly, and overly nested. A cool little trick to solve that, which I like to do, is simply add a default kwarg &lt;code&gt;level&lt;/code&gt; to &lt;code&gt;debug&lt;/code&gt; and return a &lt;a href="https://docs.python.org/2/library/functools.html#functools.partial"&gt;partial&lt;/a&gt;. A partial is a "non-complete function call" that includes a function and some arguments, so that they are passed around as one object without actually calling the function yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'debug'&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;f&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# we tell wraps that the function we are wrapping is f
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_f_as_called&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; was called with arguments=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and kwargs=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kwargs&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="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&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;'&lt;/span&gt;&lt;span class="si"&gt;{&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; return value &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_f_as_called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the decorator can work as normal:&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;@&lt;/span&gt;&lt;span class="n"&gt;debug&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use debug logging. Or, you could override the log level:&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;@&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'warning'&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Logging to the Cloud
&lt;/h1&gt;

&lt;p&gt;Writing your logs to a hosted log aggregation service makes your life easier, so you can focus on building better software, not debugging. &lt;/p&gt;

&lt;p&gt;Disclaimer: I’m a current employee @ Timber. This section is entirely optional, but I sincerely believe that logging to the cloud will make your life easier (and you can try it for completely free).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;timber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timber&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;timber_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimberHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timber_handler&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. All you need to do is get your API key from timber.io, and you’ll be able to see your logs. We automatically capture them from the logging module (and seamlessly add context), so you can continue to log normally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Pfx3BIg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.ctfassets.net/h6vh38q7qvzk/5BUP5dDcrKae4yyaoy8ocE/ba33ae45edec6325109f05a44407a2e2/footer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Pfx3BIg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.ctfassets.net/h6vh38q7qvzk/5BUP5dDcrKae4yyaoy8ocE/ba33ae45edec6325109f05a44407a2e2/footer.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>decorators</category>
      <category>python</category>
      <category>debugging</category>
    </item>
    <item>
      <title>An Intro to Web Scraping with lxml and Python</title>
      <dc:creator>Timber Staff</dc:creator>
      <pubDate>Mon, 11 Jun 2018 14:59:26 +0000</pubDate>
      <link>https://dev.to/timber/an-intro-to-web-scraping-with-lxml-and-python-32hl</link>
      <guid>https://dev.to/timber/an-intro-to-web-scraping-with-lxml-and-python-32hl</guid>
      <description>&lt;p&gt;Why should you even bother learning how to web scrape? If your job doesn't require you to learn it, then let me give you some motivation. What if you want to create a website which curates cheapest products from Amazon, Walmart and a couple of other online stores? A lot of these online stores don't provide you with an easy way to access their information using an API. In the absence of an API, your only choice is to create a web scraper which can extract information from these websites automatically and provide you with that information in an easy to use way.&lt;/p&gt;

&lt;p&gt;Here is an example of a typical API response in JSON. This is the response from Reddit:&lt;/p&gt;

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

&lt;p&gt;There are a lot of Python libraries out there which can help you with web scraping. There is &lt;a href="http://lxml.de/" rel="noopener noreferrer"&gt;lxml&lt;/a&gt;, &lt;a href="https://www.crummy.com/software/BeautifulSoup/?" rel="noopener noreferrer"&gt;BeautifulSoup&lt;/a&gt; and a full-fledged framework called &lt;a href="https://scrapy.org/" rel="noopener noreferrer"&gt;Scrapy&lt;/a&gt;. Most of the tutorials discuss BeautifulSoup and Scrapy, so I decided to go with lxml in this post. I will teach you the basics of XPaths and how you can use them to extract data from an HTML document. I will take you through a couple of different examples so that you can quickly get up-to-speed with lxml and XPaths.&lt;/p&gt;

&lt;p&gt;If you are a gamer, you will already know of (and likely love) this website. We will be trying to extract data from &lt;a href="https://store.steampowered.com/" rel="noopener noreferrer"&gt;Steam&lt;/a&gt;. More specifically, we will be selecting from the "&lt;a href="https://store.steampowered.com/explore/new/" rel="noopener noreferrer"&gt;popular new releases&lt;/a&gt;" information. I am converting this into a two-part series. In this part, we will be creating a Python script which can extract the names of the games, the prices of the games, the different tags associated with each game and the target platforms. In the second part, we will turn this script into a Flask based API and then host it on Heroku.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 1: Exploring Steam
&lt;/h3&gt;

&lt;p&gt;First of all, open up the "&lt;a href="https://store.steampowered.com/explore/new/" rel="noopener noreferrer"&gt;popular new releases&lt;/a&gt;" page on Steam and scroll down until you see the Popular New Releases tab. At this point, I usually open up Chrome developer tools and see which HTML tags contain the required data. I extensively use the element inspector tool (The button in the top left of the developer tools). It allows you to see the HTML markup behind a specific element on the page with just one click. As a high-level overview, everything on a web page is encapsulated in an HTML tag and tags are usually nested. You need to figure out which tags you need to extract the data from and you are good to go. In our case, if we take a look, we can see that every separate list item is encapsulated in an anchor (&lt;code&gt;a&lt;/code&gt;) tag.&lt;/p&gt;

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

&lt;p&gt;The anchor tags themselves are encapsulated in the &lt;code&gt;div&lt;/code&gt; with an id of &lt;code&gt;tab_newreleases_content&lt;/code&gt;. I am mentioning the id because there are two tabs on this page. The second tab is the standard "New Releases" tab, and we don't want to extract information from that tab. Hence, we will first extract the "Popular New Releases" tab, and then we will extract the required information from this tag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Start writing a Python script
&lt;/h3&gt;

&lt;p&gt;This is a perfect time to create a new Python file and start writing down our script. I am going to create a &lt;code&gt;scrape.py&lt;/code&gt; file. Now let's go ahead and import the required libraries. The first one is the &lt;a href="http://docs.python-requests.org/" rel="noopener noreferrer"&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; library and the second one is the &lt;a href="http://lxml.de/" rel="noopener noreferrer"&gt;&lt;code&gt;lxml.html&lt;/code&gt;&lt;/a&gt; library.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you don't have &lt;code&gt;requests&lt;/code&gt; installed, you can easily install it by running this command in the terminal:&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The requests library is going to help us open the web page in Python. We could have used &lt;code&gt;lxml&lt;/code&gt; to open the HTML page as well but it doesn't work well with all web pages so to be on the safe side I am going to use &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let's open up the web page using requests and pass that response to &lt;code&gt;lxml.html.fromstring&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://store.steampowered.com/explore/new/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lxml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides us with an object of &lt;code&gt;HtmlElement&lt;/code&gt; type. This object has the &lt;code&gt;xpath&lt;/code&gt; method which we can use to query the HTML document. This provides us with a structured way to extract information from an HTML document. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Fire up the Python Interpreter
&lt;/h3&gt;

&lt;p&gt;Now save this file and open up a terminal. Copy the code from the &lt;code&gt;scrape.py&lt;/code&gt; file and paste it in a Python interpreter session.&lt;/p&gt;

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

&lt;p&gt;We are doing this so that we can quickly test our XPaths without continuously editing, saving and executing our &lt;code&gt;scrape.py&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Let's try writing an XPath for extracting the div which contains the 'Popular New Releases' tab. I will explain the code as we go along:&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;new_releases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;//div[@id=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_newreleases_content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="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;This statement will return a list of all the &lt;code&gt;divs&lt;/code&gt; in the HTML page which have an id of &lt;code&gt;tab_newreleases_content&lt;/code&gt;. Now because we know that only one div on the page has this id we can take out the first element from the list (&lt;code&gt;[0]&lt;/code&gt;) and that would be our required &lt;code&gt;div&lt;/code&gt;. Let's break down the &lt;code&gt;xpath&lt;/code&gt; and try to understand it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;//&lt;/code&gt; these double forward slashes tell &lt;code&gt;lxml&lt;/code&gt; that we want to search for all tags in the HTML document which match our requirements/filters. Another option was to use &lt;code&gt;/&lt;/code&gt; (a single forward slash). The single forward slash returns only the immediate child tags/nodes which match our requirements/filters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;div&lt;/code&gt; tells &lt;code&gt;lxml&lt;/code&gt; that we are searching for &lt;code&gt;divs&lt;/code&gt; in the HTML page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[@id="tab_newreleases_content"]&lt;/code&gt; tells &lt;code&gt;lxml&lt;/code&gt; that we are only interested in those &lt;code&gt;divs&lt;/code&gt; which have an id of &lt;code&gt;tab_newreleases_content&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cool! We have got the required &lt;code&gt;div&lt;/code&gt;. Now let's go back to chrome and check which tag contains the titles of the releases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Extract the titles &amp;amp; prices
&lt;/h3&gt;

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

&lt;p&gt;The title is contained in a div with a class of &lt;code&gt;tab_item_name&lt;/code&gt;. Now that we have the "Popular New Releases" tab extracted we can run further XPath queries on that tab. Write down the following code in the same Python console which we previously ran our code in:&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;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]/text()&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;This gives us with the titles of all of the games in the "Popular New Releases" tab. Here is the expected output:&lt;/p&gt;

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

&lt;p&gt;Let's break down this XPath a little bit because it is a bit different from the last one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt; tells lxml that we are only interested in the tags which are the children of the &lt;code&gt;new_releases&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[@class="tab_item_name"]&lt;/code&gt; is pretty similar to how we were filtering &lt;code&gt;divs&lt;/code&gt; based on &lt;code&gt;id&lt;/code&gt;. The only difference is that here we are filtering based on the class name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/text()&lt;/code&gt; tells lxml that we want the text contained within the tag we just extracted. In this case, it returns the title contained in the div with the &lt;code&gt;tab_item_name&lt;/code&gt; class name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to extract the prices for the games. We can easily do that by running 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="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discount_final_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]/text()&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't think I need to explain this code as it is pretty similar to the title extraction code. The only change we made is the change in the class name.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 5: Extracting tags
&lt;/h3&gt;

&lt;p&gt;Now we need to extract the tags associated with the titles. Here is the HTML markup:&lt;/p&gt;

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

&lt;p&gt;Write down the following code in the Python terminal to extract the tags:&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;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_top_tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total_tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what we are doing here is that we are extracting the &lt;code&gt;divs&lt;/code&gt; containing the tags for the games. Then we loop over the list of extracted tags and then extract the text from those tags using the &lt;a href="http://lxml.de/lxmlhtml.html#html-element-methods" rel="noopener noreferrer"&gt;&lt;code&gt;text_content()&lt;/code&gt;&lt;/a&gt; method. &lt;code&gt;text_content()&lt;/code&gt; returns the text contained within an HTML tag without the HTML markup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We could have also made use of a list comprehension to make that code shorter. I wrote it down in this way so that even those who don't know about list comprehensions can understand the code. Eitherways, this is the alternate 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="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&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="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_top_tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Lets separate the tags in a list as well so that each tag is a separate element:&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;tags&lt;/span&gt; &lt;span class="o"&gt;=&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Extracting the platforms
&lt;/h3&gt;

&lt;p&gt;Now the only thing remaining is to extract the platforms associated with each title. Here is the HTML markup:&lt;/p&gt;

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

&lt;p&gt;The major difference here is that the platforms are not contained as texts within a specific tag. They are listed as the class name. Some titles only have one platform associated with them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img win"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While some titles have 5 platforms associated with them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img win"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img mac"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img linux"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img hmd_separator"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"HTC Vive"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img htcvive"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Oculus Rift"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"platform_img oculusrift"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see these &lt;code&gt;spans&lt;/code&gt; contain the platform type as the class name. The only common thing between these &lt;code&gt;spans&lt;/code&gt; is that all of them contain the &lt;code&gt;platform_img&lt;/code&gt; class. First of all, we will extract the &lt;code&gt;divs&lt;/code&gt; with the &lt;code&gt;tab_item_details&lt;/code&gt; class, then we will extract the &lt;code&gt;spans&lt;/code&gt; containing the &lt;code&gt;platform_img&lt;/code&gt; class and finally we will extract the second class name from those &lt;code&gt;spans&lt;/code&gt;. Here is the 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="n"&gt;platforms_div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total_platforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;platforms_div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//span[contains(@class, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;platform_img&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;platforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&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;class&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hmd_separator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;platforms&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hmd_separator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total_platforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;line 1&lt;/strong&gt; we start with extracting the &lt;code&gt;tab_item_details&lt;/code&gt; &lt;code&gt;div&lt;/code&gt;. The XPath in &lt;strong&gt;line 5&lt;/strong&gt; is a bit different. Here we have &lt;code&gt;[contains(@class, "platform_img")]&lt;/code&gt; instead of simply having &lt;code&gt;[@class="platform_img"]&lt;/code&gt;. The reason is that &lt;code&gt;[@class="platform_img"]&lt;/code&gt; returns those &lt;code&gt;spans&lt;/code&gt; which only have the &lt;code&gt;platform_img&lt;/code&gt; class associated with them. If the &lt;code&gt;spans&lt;/code&gt; have an additional class, they won't be returned. Whereas &lt;code&gt;[contains(@class, "platform_img")]&lt;/code&gt; filters all the &lt;code&gt;spans&lt;/code&gt; which have the &lt;code&gt;platform_img&lt;/code&gt; class. It doesn't matter whether it is the only class or if there are more classes associated with that tag.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;line 6&lt;/strong&gt; we are making use of a list comprehension to reduce the code size. The &lt;code&gt;.get()&lt;/code&gt; method allows us to extract an attribute of a tag. Here we are using it to extract the &lt;code&gt;class&lt;/code&gt; attribute of a &lt;code&gt;span&lt;/code&gt;. We get a string back from the &lt;code&gt;.get()&lt;/code&gt; method. In case of the first game, the string being returned is &lt;code&gt;platform_img win&lt;/code&gt; so we split that string based on the comma and the whitespace, and then we store the last part (which is the actual platform name) of the split string in the list.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;lines 7-8&lt;/strong&gt; we are removing the &lt;code&gt;hmd_separator&lt;/code&gt; from the list if it exists. This is because &lt;code&gt;hmd_separator&lt;/code&gt; is not a platform. It is just a vertical separator bar used to separate actual platforms from VR/AR hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Conclusion
&lt;/h3&gt;

&lt;p&gt;This is the code we have so far:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://store.steampowered.com/explore/new/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lxml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;new_releases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;//div[@id=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_newreleases_content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]/text()&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discount_final_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]/text()&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&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="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_top_tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;platforms_div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_releases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//div[@class=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tab_item_details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total_platforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;platforms_div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//span[contains(@class, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;platform_img&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;platforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&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;class&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hmd_separator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;platforms&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hmd_separator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total_platforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we just need this to return a JSON response so that we can easily turn this into a Flask based API. Here is the 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="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_platforms&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&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;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;platforms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is self-explanatory. We are using the &lt;a href="https://docs.python.org/3/library/functions.html#zip" rel="noopener noreferrer"&gt;&lt;code&gt;zip&lt;/code&gt;&lt;/a&gt; function to loop over all of those lists in parallel. Then we create a dictionary for each game and assign the title, price, tags, and platforms as a separate key in that dictionary. Lastly, we append that dictionary to the output list.&lt;/p&gt;

&lt;p&gt;In a future post, we will take a look at how we can convert this into a Flask based API and host it on Heroku.&lt;/p&gt;

&lt;p&gt;This article was written by &lt;a href="http://yasoob.me" rel="noopener noreferrer"&gt;Yasoob&lt;/a&gt; from &lt;a href="https://pythontips.com" rel="noopener noreferrer"&gt;Python Tips&lt;/a&gt;. I hope you guys enjoyed this tutorial. If you want to read more tutorials of a similar nature, please go to &lt;a href="https://pythontips.com" rel="noopener noreferrer"&gt;Python Tips&lt;/a&gt;. I regularly write Python tips, tricks, and tutorials on that blog. And if you are interested in learning intermediate Python, then please check out my open source book &lt;a href="http://book.pythontips.com" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have a great day!&lt;/p&gt;

&lt;p&gt;Just a disclaimer: we're a logging company &lt;a href="https://timber.io" rel="noopener noreferrer"&gt;here @ Timber&lt;/a&gt;. We'd love it if you tried out our product (it's seriously great!), but that's all we're going to advertise it.&lt;/p&gt;

</description>
      <category>lxml</category>
      <category>python</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
