<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>dsillman</title>
 <link href="/atom.xml" rel="self"/>
 <link href="https://www.dsillman.com/"/>
 <updated>2026-06-20T00:27:08+00:00</updated>
 <id>https://www.dsillman.com</id>
 <author>
   <name>David Sillman</name>
   <email></email>
 </author>

 
 <entry>
   <title>TF-GCP II: Uploading static content to GCP storage</title>
   <link href="https://www.dsillman.com/2025/03/16/gcpstaticsite-ii/"/>
   <updated>2025-03-16T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2025/03/16/gcpstaticsite-ii</id>
   <content type="html">&lt;p&gt;In the last article, we were able to successfully deploy a Google Cloud Storage (GCS) bucket using Terraform. It was a little bit anticlimactic, since the only real indication that we had done anything at all was the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil ls&lt;/code&gt; showing that the bucket was created. In this article, we’ll get closer to our goal of hosting a static site on GCS by getting our implementation to the point where there’s actually a URL we can visit and see some static site content.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
First, we’ll need to decide what that static content which is served from GCS will be.&lt;/p&gt;

&lt;h2 id=&quot;a-basic-hello-world-site-with-tailwind&quot;&gt;A basic “hello world” site, with Tailwind&lt;/h2&gt;

&lt;p&gt;For the purposes of this tutorial, we’re not doing anything particularly fancy. In short, we’re going to write a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file which, when viewed in the browser, looks half-decent. One of the ways we can do that in a low-effort way is by using &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;, a utility-first CSS framework that allows us to style our site entirely within HTML without needing to bother with CSS files.&lt;/p&gt;

&lt;p&gt;This article isn’t meant to go into depth about how Tailwind works, but I’ll at least give a basic explanation of the &lt;a href=&quot;https://tailwindcss.com/docs/styling-with-utility-classes&quot;&gt;&lt;em&gt;utility classes&lt;/em&gt;&lt;/a&gt; which we’re using for our “hello world” example to make it look nice.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;
First, I’ll create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content/&lt;/code&gt; directory in my project folder which I’ll use to store the static content for my site. Inside that directory, I’ll create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file with the following content:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width, initial-scale=1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello, world!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdn.jsdelivr.net/npm/tailwindcss@2.0.2/dist/tailwind.min.css&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Body goes here --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a super basic skeleton of an empty HTML page. The only thing we’ve set up is a link to the Tailwind CSS CDN, which will allow us to use Tailwind utility classes to style our site within the document. Additionally, I’ve added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag with the text “Hello, world!” which will be displayed in the browser tab.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
For the body, I let Copilot take the wheel and generate a really basic centered “Hello World” message:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bg-gray-100 h-screen flex items-center justify-center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-4xl text-gray-800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll note the use of a bunch of non-sensical tokens like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-gray-100&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;items-center&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-4xl&lt;/code&gt;. These are the Tailwind &lt;em&gt;utility classes&lt;/em&gt; I mentioned earlier. They’re a way of applying styles to elements in a way that’s more readable and maintainable than writing CSS. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-gray-100&lt;/code&gt; sets the background color of the element to a light gray, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;items-center&lt;/code&gt; centers the content vertically, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-4xl&lt;/code&gt; sets the text size to “quadruple extra large.”&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
When I open this HTML document from my local filesystem in a browser, I see a centered “Hello World” message in a nice, large font:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;centered-image&quot; src=&quot;/assets/images/content-index-html.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looks like it will do the trick. For good measure, I’ll make a copy of this file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content/404.html&lt;/code&gt;, replacing the “Hello World” message(s) with “404,” to act as a fallback page in case the user tries to access a page that doesn’t exist:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bg-gray-100 h-screen flex items-center justify-center&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-4xl text-gray-800&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;404&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Now, let’s get this content uploaded to our GCS bucket - but via Terraform, of course.&lt;/p&gt;

&lt;h2 id=&quot;gcs-bucket-object-resources&quot;&gt;GCS bucket object resources&lt;/h2&gt;

&lt;p&gt;I’m going to create a new dedicated Terraform file for managing the content of my GCS bucket. I’ll called it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site_content.tf&lt;/code&gt;. With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google&lt;/code&gt; provider, objects in cloud storage themselves can be provisioned as &lt;em&gt;resources&lt;/em&gt; in Terraform. So, in this new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site_content.tf&lt;/code&gt; file, I’ll define two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket_object&lt;/code&gt; resources, one for each of the HTML files I created earlier:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_object&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index_html&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google_storage_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;static_site_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index.html&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content/index.html&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;text/html&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_object&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;error_html&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google_storage_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;static_site_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;404.html&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content/404.html&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;text/html&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, these resources look a little different from the ones we’ve been declaring in the previous article. You’ll note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; configuration is not being set to an explicit value. Rather, it’s being set according to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; attribute of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource we created in the last article, called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static_site_bucket&lt;/code&gt;. So, the syntax for referencing attributes of other resources in Terraform is:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;resource_type&amp;gt;.&amp;lt;resource_name&amp;gt;.&amp;lt;attribute_name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;google_storage_bucket&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;static_site_bucket&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may ask, “why not just set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; attribute to the name of the bucket directly?” The answer is that this way, we can ensure that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket_object&lt;/code&gt; resources are created in the same bucket as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource, even if the name of the bucket changes. Moreover, Terraform also takes stock of referential dependencies between resources like this one, which helps it determine the correct order in which to create resources, and which resources can be provisioned in parallel. So, implicitly, we are telling Terraform that these new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket_object&lt;/code&gt; resources depend on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource we created earlier, so they’ll be created in sequence.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content_type&lt;/code&gt; arguments tell GCP what to name the object in the bucket and what type of content it is, respectively. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; argument is the path to the file on the local filesystem which we want to upload to the bucket. In this case, it’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;404.html&lt;/code&gt; files in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
We’re finally ready to validate the execution plan with Terraform’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plan&lt;/code&gt; command.&lt;/p&gt;

&lt;details&gt;
&lt;summary&gt;&lt;code&gt;terraform plan&lt;/code&gt; output&lt;/summary&gt;
&lt;div&gt;

    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform plan

  Terraform used the selected providers to generate the   following execution plan. Resource actions are indicated   with the following symbols:
    + create
  
  Terraform will perform the following actions:
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket.static_site_bucket will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + effective_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + force_destroy               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;                          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + location                    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;US&quot;&lt;/span&gt;
        + name                        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + project                     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + project_number              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + public_access_prevention    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + rpo                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + self_link                   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + storage_class               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;
        + terraform_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + uniform_bucket_level_access &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + url                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + soft_delete_policy &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + versioning &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + website &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + main_page_suffix &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index.html&quot;&lt;/span&gt;
            + not_found_page   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;404.html&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket_object.error_html will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_object&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;error_html&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + bucket         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + content        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;sensitive value&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + content_type   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;text/html&quot;&lt;/span&gt;
        + crc32c         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + detect_md5hash &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;different hash&quot;&lt;/span&gt;
        + generation     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + kms_key_name   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + md5hash        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + media_link     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + name           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;404.html&quot;&lt;/span&gt;
        + output_name    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + self_link      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content/404.html&quot;&lt;/span&gt;
        + storage_class  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket_object.index_html will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_object&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index_html&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + bucket         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + content        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;sensitive value&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + content_type   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;text/html&quot;&lt;/span&gt;
        + crc32c         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + detect_md5hash &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;different hash&quot;&lt;/span&gt;
        + generation     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + kms_key_name   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + md5hash        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + media_link     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + name           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index.html&quot;&lt;/span&gt;
        + output_name    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + self_link      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content/index.html&quot;&lt;/span&gt;
        + storage_class  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c&quot;&gt;# random_uuid.bucket_uuid will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;random_uuid&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bucket_uuid&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + result &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
  Plan: 4 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

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

&lt;p&gt;Recall that, at the end of the last article, I destroyed everything in my GCP project. So when I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt;, Terraform correctly queried the remote GCP state to determine that all of my resources need to be re-created. This is a good sign that our Terraform configuration is working as expected. We also see our expected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket_object&lt;/code&gt; resources being created, along with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website&lt;/code&gt; block in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource. Let’s apply the changes.&lt;/p&gt;

&lt;details&gt;
&lt;summary&gt;&lt;code&gt;terraform apply&lt;/code&gt; output&lt;/summary&gt;
&lt;div&gt;

    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform apply
  random_uuid.bucket_uuid: Creating...
  random_uuid.bucket_uuid: Creation &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 0s   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;be028675-b3cd-8fbd-c48a-3ddb6629ca7b]
  google_storage_bucket.static_site_bucket: Creating...
  google_storage_bucket.static_site_bucket: Creation &lt;span class=&quot;nb&quot;&gt;complete   &lt;/span&gt;after 2s   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket]
  google_storage_bucket_object.error_html: Creating...
  google_storage_bucket_object.index_html: Creating...
  google_storage_bucket_object.error_html: Creation &lt;span class=&quot;nb&quot;&gt;complete   &lt;/span&gt;after 1s   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket-404.  html]
  google_storage_bucket_object.index_html: Creation &lt;span class=&quot;nb&quot;&gt;complete   &lt;/span&gt;after 1s   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket-index.  html]
  
  Apply &lt;span class=&quot;nb&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; Resources: 4 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

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

&lt;p&gt;&lt;br /&gt;
I can once again confirm that the bucket is created with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil ls&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gsutil &lt;span class=&quot;nb&quot;&gt;ls
&lt;/span&gt;gs://be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Specifically, I can also see the objects in the bucket:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gsutil &lt;span class=&quot;nb&quot;&gt;ls &lt;/span&gt;gs://be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
gs://be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket/404.html
gs://be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
It correctly identifies that we’ve uploaded the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;404.html&lt;/code&gt; files to the bucket. Now, let’s see if we can access the site content via the bucket URL.&lt;/p&gt;

&lt;h2 id=&quot;viewing-the-site-content&quot;&gt;Viewing the site content&lt;/h2&gt;

&lt;p&gt;To view the site content, I’ll navigate to the URL of the bucket in my browser. The URL is in the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&amp;lt;bucket-name&amp;gt;.storage.googleapis.com&lt;/code&gt;. In this case, the bucket name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket&lt;/code&gt;, so the URL is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://be028675-b3cd-8fbd-c48a-3ddb6629ca7b-site-bucket.storage.googleapis.com/index.html&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; around this point in the exercise, I decided to destroy and re-apply my terraform project, which regenerated my UUID and bucket name. This may continue to happen insofar as randomness is involved in the generation of the bucket name. If you’re following along, please overlook any discrepancies in the bucket name within and between articles.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I navigate to this URL, I get an error message:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Error&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Code&amp;gt;&lt;/span&gt;AccessDenied&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Access denied.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Details&amp;gt;&lt;/span&gt;Anonymous caller does not have storage.objects.get access to the Google Cloud Storage object. Permission &apos;storage.objects.get&apos; denied on resource (or it may not exist).&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Details&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Error&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This indicates that, while we’ve successfully uploaded the content to the bucket, the bucket is not publicly accessible, and so our URL is returning an access denied error. We’ll need to update the bucket’s permissions to allow public access to the objects within it.&lt;/p&gt;

&lt;h2 id=&quot;making-the-bucket-public&quot;&gt;Making the bucket public&lt;/h2&gt;

&lt;p&gt;To make the bucket public, we’ll need to update the bucket’s IAM policy to allow all users to view the objects within it. We can do this by adding a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket_iam_binding&lt;/code&gt; resource to our Terraform configuration. I’ll add this to our existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_iam_binding&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;public_read&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google_storage_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;static_site_bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;roles/storage.objectViewer&quot;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;allUsers&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note again that we are using the resource attribute reference syntax to set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; attribute to the name of the bucket we created earlier. This ensures that the IAM binding is created for the same bucket as the objects we uploaded.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;role&lt;/code&gt; attribute is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles/storage.objectViewer&lt;/code&gt;, which is a predefined IAM role that grants permission to view objects in a bucket. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;members&lt;/code&gt; attribute is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allUsers&lt;/code&gt;, which is a special identifier that represents all users, including anonymous users. This means that any user who visits the bucket URL will be able to view the objects within the bucket.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
We can check what the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt; command looks like in order to confirm that the IAM binding will be created as expected for our bucket.&lt;/p&gt;

&lt;details&gt;
&lt;summary&gt;&lt;code&gt;terraform plan&lt;/code&gt; output&lt;/summary&gt;
&lt;div&gt;

    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform plan
  random_uuid.bucket_uuid: Refreshing state...   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;edb54251-622d-bbec-5005-03ddfd1117fa]
  google_storage_bucket.static_site_bucket: Refreshing state...   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;edb54251-622d-bbec-5005-03ddfd1117fa-site-bucket]
  google_storage_bucket_object.index_html: Refreshing state...   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;edb54251-622d-bbec-5005-03ddfd1117fa-site-bucket-index.  html]
  google_storage_bucket_object.error_html: Refreshing state...   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;edb54251-622d-bbec-5005-03ddfd1117fa-site-bucket-404.html]
  
  Terraform used the selected providers to generate the   following execution plan. Resource actions are indicated with   the following symbols:
    + create
  
  Terraform will perform the following actions:
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket_iam_binding.public_read will be   created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket_iam_binding&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;public_read&quot;&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + bucket  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;edb54251-622d-bbec-5005-03ddfd1117fa-site-bucket&quot;&lt;/span&gt;
        + etag    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + members &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;allUsers&quot;&lt;/span&gt;,
          &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
        + role    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;roles/storage.objectViewer&quot;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
  Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

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

&lt;p&gt;&lt;br /&gt;
Looks good to me. Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;, we see that the policy binding gets successfully created.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform apply
  google_storage_bucket_iam_binding.public_read: Creating...
  google_storage_bucket_iam_binding.public_read: Creation &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 5s &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;b/edb54251-622d-bbec-5005-03ddfd1117fa-site-bucket/roles/storage.objectViewer]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Now, when I navigate to the bucket URL in my browser, I see the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file displayed:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;centered-image&quot; src=&quot;/assets/images/hello-world-site-shot.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success! We’ve successfully uploaded static content to a GCS bucket and made it publicly accessible. I didn’t promise that the URL used to access the website would necessarily be a nice one - so our goal was accomplished, as stated! In the next article, we’ll take a moment to look back over the terraform we’ve written so far and see if there are any improvements we can make.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Once again, since I’m taking a break from this project, I’ll clean up after myself to save myself any risk of incurring cloud costs while I’m not using the resources. As with the last article, it’s easy to destroy the bucket and IAM binding with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform destroy&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>TF-GCP I: Provisioning GCP resources with Terraform</title>
   <link href="https://www.dsillman.com/2025/03/09/gcpstaticsite/"/>
   <updated>2025-03-09T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2025/03/09/gcpstaticsite</id>
   <content type="html">&lt;p&gt;I was recently challenged at work to come up with a long-term personal development goal for the year. I suck at doing that stuff. But as our team is moving increasingly towards cloud computing, I &lt;em&gt;do know&lt;/em&gt; that eventually I will hit a ceiling if I don’t learn &lt;em&gt;something&lt;/em&gt; about building complex systems in the Cloud. This article is my first stab at that on my own dime, and with my own time.&lt;/p&gt;

&lt;p&gt;My team at work is using Amazon (AWS) as the cloud platform for all of our services. This is probably the most economical choice for most purposes, but why take the path most traveled? I wanted to try something new and learn about some of the differences between AWS and Google’s cloud platform, GCP (Google Cloud Platform), so I decided I’d figure out how to use Terraform in that context.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;centered-image dark-inverted&quot; alt=&quot;GCP Terraform&quot; src=&quot;/assets/images/gcp_terraform.jpeg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One of the most basic things you can do in the Cloud is set up a publicly-accessible static website by using cloud storage. Basically, you’re just uploading static files to a cloud server and configuring it to serve those files to the public using a managed URL. This is a pretty straightforward way to host all sorts of things from blogs, resumes or project documentation.&lt;/p&gt;

&lt;h2 id=&quot;how-they-tell-you-to-do-it-vs-how-you-should-do-it&quot;&gt;How they tell you to do it, vs. how you should do it&lt;/h2&gt;

&lt;p&gt;If you follow any tutorials on GCP (or any other cloud platform), they’ll explain how to do this using the Google Cloud Console (or AWS cloud console for AWS), which is just a UI interface for managing cloud resources. This is great for learning how things work, but it’s not a great way to manage your infrastructure in the long term. Ideally, your infrastructure specs would be layed out in code and tracked in version control, so that you can see how your infrastructure has changed over time and roll back changes if necessary.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Terraform&lt;/strong&gt; comes in. Terraform is a DSL (domain-specific language) for defining the cloud resources managed by a project. The cloud resources could straddle many different cloud providers, but in this case we’re just using GCP. Learning Terraform is generally a good way to learn about how different cloud resources can interact with one another to build a larger, multi-component system.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Thankfully, a static site doesn’t require more than one component - our provisioned cloud storage. So this project shouldn’t be too complicated…&lt;/p&gt;

&lt;p&gt;…right?&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;&lt;br /&gt;
There are a few things that took me a little bit to realize I needed installed as I was getting started:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Terraform CLI&lt;/strong&gt;: AKA the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform&lt;/code&gt; CLI. If you’re using Mac OSX, you can install this with Homebrew. But installation for all other platforms is pretty straightforward. You can find the installation instructions on &lt;a href=&quot;https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli&quot;&gt;their website&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;br /&gt;
 If you successfully managed to install Terraform, you should be able to run the following     command in your terminal:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; terraform &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Google Cloud CLI&lt;/strong&gt;: AKA the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; CLI. On Mac OSX, you’re required to download the corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.dmg&lt;/code&gt; file from the &lt;a href=&quot;https://cloud.google.com/sdk/docs/install&quot;&gt;Google Cloud SDK&lt;/a&gt; website and follow the given instructions (basically just run a bash script).&lt;/p&gt;

    &lt;p&gt;&lt;br /&gt;
 If you successfully managed to install the Google Cloud CLI, you should be able to run     the following command in your terminal:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; gcloud &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once these two are installed, you’re more or less ready to get started. There are just a few other things which we’d want to configure about our Google Cloud setup before we start provisioning resources.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-google-cloud&quot;&gt;Setting up Google Cloud&lt;/h2&gt;

&lt;p&gt;&lt;br /&gt;
Once &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; is installed, you’ll want to authenticate your Google account with the CLI. This is done by running the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud auth login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will take you through an interactive set of web pages to authenticate your Google account with the CLI. Once you’ve authenticated, you’ll be able to run commands against your Google Cloud account.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;In Google Cloud, there’s a hierarchical structure to how resources are organized. At the top level, you (optionally) have an &lt;em&gt;Organization&lt;/em&gt;, which may contain multiple &lt;em&gt;Projects&lt;/em&gt;. Each &lt;em&gt;Project&lt;/em&gt; independently manages its permissions and resources. By default, consumer GCP accounts do not have an Organization configured, so you’ll create projects in the root of your account (no Organization containing them).&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
To get started, we’ll create a &lt;em&gt;Project&lt;/em&gt; for the static site we want to host on GCP. I’ll call my project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;davids-static-site&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud projects create davids-static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
After creating the project, wait about 30 - 60 seconds for the project to fully materialize in Google Cloud. You can check the status of your project by running the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud projects describe davids-static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Once that project is created, meaning its status is “ACTIVE” in the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;describe&lt;/code&gt; command, you can set it as the default project for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; CLI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;project davids-static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last step in ensuring our account and project are properly set up is to enable billing for the project. This is required to provision any resources in GCP. If you don’t currently have a billing account associated with your Google Cloud account, it’s easiest to do that from the Google Cloud console web interface.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You didn’t think you were actually going to do anything in the Cloud without paying for it, did you?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br /&gt;
After you have a billing account and confirmed a form of payment, you can enable billing for the project by running the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Check your available billing account IDs&lt;/span&gt;
gcloud billing accounts list
&lt;span class=&quot;c&quot;&gt;# Set one to use for the new project&lt;/span&gt;
gcloud billing projects &lt;span class=&quot;nb&quot;&gt;link &lt;/span&gt;davids-static-site &lt;span class=&quot;nt&quot;&gt;--billing-account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;billing-account-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Now that we have a project and billing set up for our account, we can start provisioning resources in GCP. We’re finally ready to start tinkering with Terraform!&lt;/p&gt;

&lt;h2 id=&quot;setting-up-our-terraform-project&quot;&gt;Setting up our Terraform project&lt;/h2&gt;

&lt;p&gt;&lt;br /&gt;
Alright, first things first. I’m going to create a fresh directory for the project where I like to organize these things, which is in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME/terraform-projects&lt;/code&gt; directory. I’ll call this project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcp-static-site&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/terraform-projects/gcp-static-site
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/terraform-projects/gcp-static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The way that I’m choosing to split my code across files is more or less arbitrary. I’m following some conventions I’ve learned from co-workers and from reading the Terraform documentation. I’m sure there are many ways to organize your Terraform code, but this is just one way that I think is pretty clean.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first thing I do to set up my project is to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;versions.tf&lt;/code&gt; file. The only thing I’ll put in this file is the &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform&lt;/code&gt; block&lt;/strong&gt; for our project. This block specifies the version of Terraform that we’re using, and it’s a good idea to specify this in every Terraform project you create. I also use it to specify the required version of the Google Cloud provider plugin.&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;required_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;gt;= 1.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hashicorp/google&quot;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;6.24.0&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Now that we have this config block in our directory, we can just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform init&lt;/code&gt; to instruct Terraform to read it and download the required provider plugin for Google Cloud.&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform init&lt;/code&gt; output&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform init
  Initializing the backend...
  Initializing provider plugins...
  - Finding hashicorp/google versions matching &lt;span class=&quot;s2&quot;&gt;&quot;6.24.0&quot;&lt;/span&gt;...
  - Installing hashicorp/google v6.24.0...
  - Installed hashicorp/google v6.24.0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;signed by HashiCorp&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Terraform has created a lock file .terraform.lock.hcl to record the 
  provider selections it made above. Include this file &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;your version control 
  repository so that Terraform can guarantee to make the same selections by default 
  when you run &lt;span class=&quot;s2&quot;&gt;&quot;terraform init&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the future.
  
  Terraform has been successfully initialized!
  
  You may now begin working with Terraform. Try running &lt;span class=&quot;s2&quot;&gt;&quot;terraform plan&quot;&lt;/span&gt; to 
  see any changes that are required &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;your infrastructure. All Terraform 
  commands should now work.
  
  If you ever &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;or change modules or backend configuration &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Terraform,
  rerun this &lt;span class=&quot;nb&quot;&gt;command &lt;/span&gt;to reinitialize your working directory. If you forget, 
  other commands will detect it and remind you to &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;so &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;necessary.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;This should have done a few things. First, you should see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.terraform&lt;/code&gt; directory in the project, which basically saves the downloaded provider plugins and other state information. Second, you should see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.terraform.lock.hcl&lt;/code&gt; file, which is a lock file that specifies the exact versions of the provider plugins that were downloaded. I think of this as analogous to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poetry.lock&lt;/code&gt;, or event &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt; in Python projects.&lt;/p&gt;

&lt;h2 id=&quot;lets-provision-a-gcs-bucket&quot;&gt;Let’s provision a GCS bucket&lt;/h2&gt;

&lt;p&gt;&lt;br /&gt;
Ok, great. So our Terraform project is initialized. How can we get it to provision a Google Cloud Storage (GCS) bucket for us?&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
A GCS bucket is essentially just a shared storage location, like a mini file system which can be accessed via API requests, much like Amazon’s equivalent Simple Storage Service (S3).&lt;/p&gt;

&lt;p&gt;Since our project is essentially just using this cloud storage to host some static site pages, we’ll obviously want the ownership of this storage bucket to fall within our project. So, let’s create a dedicated Terraform file for provisioning the GCS bucket, called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket.tf&lt;/code&gt;, with a &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; block&lt;/strong&gt; defined in it:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;site-bucket&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;US&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;force_destroy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
The syntax of a &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; block&lt;/strong&gt; is:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;resource type&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;resource name&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# resource configuration&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, above, we’re declaring a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource in the project, and we’re calling it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static_site_bucket&lt;/code&gt;. There are a minimum of two required arguments for this resource type: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; is the name of the bucket, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; is the region where the bucket will be created. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;force_destroy&lt;/code&gt; argument is optional, but it’s set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; here to allow Terraform to delete the bucket even if it’s not empty.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I’m getting an idea of what the possible arguments are for this resource type by looking at the &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket&quot;&gt;Google Cloud provider documentation&lt;/a&gt;. The Provider docs are &lt;em&gt;very valuable&lt;/em&gt; resources for understanding what you can do with a given resource type. It’s basically the only way to use Terraform effectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br /&gt;
Now that we actually have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; in our project, we can meaningfully run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt; to get a preview of what Terraform thinks it will need to provision to deploy our project.&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform plan&lt;/code&gt; output (error!)&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform plan

  Planning failed. Terraform encountered an error &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;generating this   plan.
  
  ╷
  │ Error: Invalid provider configuration
  │ 
  │ Provider &lt;span class=&quot;s2&quot;&gt;&quot;registry.terraform.io/hashicorp/google&quot;&lt;/span&gt; requires explicit 
  configuration. Add a provider block to the root module and configure the 
  provider&lt;span class=&quot;s1&quot;&gt;&apos;s required arguments as described in the
  │ provider documentation.
  │ 
  ╵
  ╷
  │ Error: Attempted to load application default credentials since neither 
  `credentials` nor `access_token` was set in the provider block.  No 
  credentials loaded. To use your gcloud credentials, run &apos;&lt;/span&gt;gcloud auth 
  application-default login&lt;span class=&quot;s1&quot;&gt;&apos;. Original error: google: could not find 
  default credentials. See https://cloud.google.com/docs/authentication/
  external/set-up-adc for more information
  │ 
  │   with provider[&quot;registry.terraform.io/hashicorp/google&quot;],
  │   on &amp;lt;empty&amp;gt; line 0:
  │   (source code not available)
  │ 

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;&lt;br /&gt;
Ok, so two issues. The first one (“Invalid provider configuration”) is because we haven’t actually configured the Google Cloud provider in our project. Whoops! That’s an easy fix we’ll get to shortly.&lt;/p&gt;

&lt;p&gt;The second one (“Attempted to load application default credentials”) is because Terraform doesn’t know how to authenticate with Google Cloud. We went through a manual process to authenticate with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; earlier, but Terraform doesn’t know about that. We need to tell Terraform to use the same credentials that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; is using. Thankfully, they give us a really easy command to just run to set up Terraform:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will cache some authenticated credentials on your local filesystem which Terraform will pick up for use in authenticating with Google Cloud. This is a one-time setup, so you shouldn’t need to run this command again unless you change your Google Cloud credentials.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Now let’s create that provider configuration in our Terraform project. I like putting these in a dedicated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; file, with one or more &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provider&lt;/code&gt; blocks&lt;/strong&gt; defined in it:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;davids-static-site&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;region&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;us-west2&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is all that’s strictly needed for Terraform to be able to use the Google Cloud provider. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;project&lt;/code&gt; argument is the name of the Google Cloud project we created earlier, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;region&lt;/code&gt; argument is the default region that resources will be created in. This is a good default to set, but it can be overridden on a per-resource basis if necessary.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Now that we have this provider configuration, we can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt; again to see what Terraform thinks it needs to do to deploy our project.&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform plan&lt;/code&gt; output&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform plan

  Terraform used the selected providers to generate the following execution 
  plan. Resource actions are indicated with the following symbols:
    + create
  
  Terraform will perform the following actions:
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket.static_site_bucket will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + effective_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + force_destroy               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;                          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + location                    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;US&quot;&lt;/span&gt;
        + name                        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;site-bucket&quot;&lt;/span&gt;
        + project                     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + project_number              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + public_access_prevention    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + rpo                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + self_link                   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + storage_class               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;
        + terraform_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + uniform_bucket_level_access &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + url                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + soft_delete_policy &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + versioning &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + website &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
  Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;Lovely! It correctly sees that, according to our project specifications, running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; ought to create a GCS storage bucket. It seems to also pick up the configurations we set in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket.tf&lt;/code&gt; file, like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; of the bucket.&lt;/p&gt;

&lt;p&gt;Do we dare try running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;? This CLI command actually executes the stated plan, submitting the necessary API requests to Google Cloud to create the resources we’ve specified (in this case, just a GCS bucket).&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform apply&lt;/code&gt; output (error!)&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform apply

  ...
  google_storage_bucket.static_site_bucket: Creating...
  ╷
  │ Error: googleapi: Error 409: The requested bucket name is not 
  available. The bucket namespace is shared by all &lt;span class=&quot;nb&quot;&gt;users &lt;/span&gt;of the system. 
  Please &lt;span class=&quot;k&quot;&gt;select &lt;/span&gt;a different name and try again., conflict
  │ 
  │   with google_storage_bucket.static_site_bucket,
  │   on bucket.tf line 1, &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt;:
  │    1: resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  │ 
  ╵
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;&lt;br /&gt;
Well that wasn’t expected. Let’s take a moment to discuss the way Google Cloud Storage buckets work.&lt;/p&gt;

&lt;h3 id=&quot;the-issue-with-gcs-buckets&quot;&gt;The issue with GCS buckets&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is frankly the most outrageous design decision I’ve seen on the part of the GCP team, especially as someone familiar with using AWS where you can choose your bucket names pretty freely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In GCP, the bucket namespace is &lt;em&gt;global&lt;/em&gt; - if somebody else has already named a GCS bucket &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site-bucket&lt;/code&gt; (the name we were trying to use), then we can’t use that name. That means that everyone has to come up with a globally unique name for their buckets, which is a bit of a pain point. Some users of GCP suggest using a UUID or other unique identifier in the bucket name to ensure uniqueness, then using prefixing or suffixing to add a human readable component to the name.&lt;/p&gt;

&lt;p&gt;Is there a way to incorporate some sort of randomness / UUID generation into our Terraform project to ensure that we can always create a bucket with a unique name? With one quick Google search, we will learn that is possible by…&lt;/p&gt;

&lt;h2 id=&quot;utilizing-terraforms-random-provider&quot;&gt;Utilizing Terraform’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random&lt;/code&gt; provider&lt;/h2&gt;

&lt;p&gt;Just like there is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google&lt;/code&gt; &lt;em&gt;Provider&lt;/em&gt; responsible for managing Google Cloud resources, &lt;em&gt;Providers&lt;/em&gt; are a more general concept in Terraform. Some providers may do things locally on your system, or talk to non-cloud services. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random&lt;/code&gt; provider is one such provider, which can generate random values for use in your Terraform project. It treats a small program which can be used to repeatably generate random values as a resource which is provisioned and maintained by Terraform, even though it’s all actually happening locally on your machine.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Because it’s just a provider like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google&lt;/code&gt;, we’ll treat it similarly and add entries to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;versions.tf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; to configure it:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# versions.tf&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hashicorp/random&quot;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3.7.1&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# providers.tf&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;random&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# No configuration needed for the random provider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random&lt;/code&gt; provider configured, we can use it to generate a random UUID for the bucket name in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket.tf&lt;/code&gt; file. I’ll still opt to suffix the random string with “site-bucket” so it’s somewhat identifiable within our project.&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# bucket.tf&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;random_uuid&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bucket_uuid&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${random_uuid.bucket_uuid.result}-site-bucket&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;US&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;force_destroy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Syntax note&lt;/strong&gt;: It’s ubiquitous in Terraform to use string-formatting syntax &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${}&lt;/code&gt; to interpolate values into strings. This is how we’re able to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random_uuid.bucket_uuid.result&lt;/code&gt; value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; attribute of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we just added a new provider to the project, we’ll need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform init&lt;/code&gt; again to download the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random&lt;/code&gt; provider plugin into our project’s local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.terraform&lt;/code&gt; cache. After running that, we’ll be able to check the impact on our Terraform plan by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt; again:&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform plan&lt;/code&gt; output&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform plan
  
  Terraform used the selected providers to generate the following execution   plan. Resource actions are indicated with the following symbols:
    + create
  
  Terraform will perform the following actions:
  
    &lt;span class=&quot;c&quot;&gt;# google_storage_bucket.static_site_bucket will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;google_storage_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;static_site_bucket&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + effective_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + force_destroy               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;                          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + location                    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;US&quot;&lt;/span&gt;
        + name                        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + project                     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + project_number              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + public_access_prevention    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + rpo                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + self_link                   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + storage_class               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;
        + terraform_labels            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            + &lt;span class=&quot;s2&quot;&gt;&quot;goog-terraform-provisioned&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        + uniform_bucket_level_access &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + url                         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + soft_delete_policy &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + versioning &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  
        + website &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
    &lt;span class=&quot;c&quot;&gt;# random_uuid.bucket_uuid will be created&lt;/span&gt;
    + resource &lt;span class=&quot;s2&quot;&gt;&quot;random_uuid&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bucket_uuid&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        + &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        + result &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;known after apply&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  
  Plan: 2 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;Note that Terraform identifies that we addeed a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random_uuid&lt;/code&gt; resource to the project, which will be created as a part of the project. This explains why the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_storage_bucket&lt;/code&gt; resource now has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; attribute that is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(known after apply)&lt;/code&gt; - it will be set to the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random_uuid&lt;/code&gt; resource after it’s created (with our suffix appended).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Fair warning&lt;/strong&gt;: from here on out, you will officially start being billed for the resources you’re provisioning in GCP. Up until this point we’ve been unsuccessful in creating any resources, so no costs were incurred. Starting with the following step, we will be creating resources in GCP, so you will start to incur costs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can give &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; a shot!&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform apply&lt;/code&gt; output&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform apply

  ...
  random_uuid.bucket_uuid: Creating...
  random_uuid.bucket_uuid: Creation &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 0s   &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de]
  google_storage_bucket.static_site_bucket: Creating...
  google_storage_bucket.static_site_bucket: Creation &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 2s &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de-site-bucket]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;Done! We’ve successfully created a GCS bucket in GCP using Terraform. We can verify this by checking the Google Cloud Console, or by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil&lt;/code&gt; commands to interact with the bucket. Specifically, if you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil ls&lt;/code&gt;, it will list the buckets in your project:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gsutil &lt;span class=&quot;nb&quot;&gt;ls
  &lt;/span&gt;gs://5c01e5eb-1bed-5c2e-9a89-1570b1b405de-site-bucket/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And there it is! Our bucket is listed in the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil ls&lt;/code&gt; command. We haven’t quite made it to the point where there’s a static site we can visit and see content. But let’s return back to this for another article later on to discuss how to actually upload content to the bucket and configure it to serve a static site publicly.&lt;/p&gt;

&lt;h2 id=&quot;closing-note&quot;&gt;Closing note&lt;/h2&gt;

&lt;p&gt;In the mean-time, we’ve still provisioned a GCS bucket in GCP. Since I’m not planning on using it for anything between now and the next article, I’ll go ahead clean up after myself with the simple Terraform command, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform destroy&lt;/code&gt;. This is one of the nicest things about Terraform in my opinion - everything you can provision, you can also destroy using a single, unified interface.&lt;/p&gt;

&lt;details&gt;

&lt;summary&gt;&lt;code&gt;terraform destroy&lt;/code&gt; output&lt;/summary&gt;

&lt;div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;terraform destroy
  random_uuid.bucket_uuid: Refreshing state... &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de]
  google_storage_bucket.static_site_bucket: Refreshing state... &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de-site-bucket]
  ...
  google_storage_bucket.static_site_bucket: Destroying... &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de-site-bucket]
  google_storage_bucket.static_site_bucket: Destruction &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 1s
  random_uuid.bucket_uuid: Destroying... &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5c01e5eb-1bed-5c2e-9a89-1570b1b405de]
  random_uuid.bucket_uuid: Destruction &lt;span class=&quot;nb&quot;&gt;complete &lt;/span&gt;after 0s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;

&lt;/details&gt;

&lt;p&gt;I can confirm that it’s cleaned up by noting the empty result when I try &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsutil ls&lt;/code&gt; again. Nice - now I can rest easy knowing that I’m not being billed for any resources in GCP. I can always come back to this project and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; to re-provision the bucket if I need to.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Modular YAML released: &lt;code&gt;yaml-extras&lt;/code&gt; on PyPI</title>
   <link href="https://www.dsillman.com/2024/12/30/yamlextras-release/"/>
   <updated>2024-12-30T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2024/12/30/yamlextras-release</id>
   <content type="html">&lt;p&gt;I’m proud to announce that I’ve made my first official contribution to the Python Package Index
(PyPI) with the release of a package called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml-extras&lt;/code&gt;. This package is the culmination of what
I’ve discussed in the previous three blog posts dedicated to loading the contents of YAML files
into one another in a modular way. See the other three articles linked below:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/22/yamlextras.html&quot;&gt;Modular YAML I: a custom “import” tag&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/25/yamlextras-ii.html&quot;&gt;Modular YAML II: importing YAML files with “import”&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/26/yamlextras-iii.html&quot;&gt;Modular YAML III: importing globs with “import-all”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
The package is available on PyPI &lt;a href=&quot;https://pypi.org/project/yaml-extras/&quot;&gt;here&lt;/a&gt;. The source code is
available and open source on Github, plus an extended documentation website:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dsillman2000/yaml-extras&quot;&gt;Github: yaml-extras&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://yaml-extras.pages.dev&quot;&gt;Documentation: yaml-extras&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
Because the package is available on PyPI, you can install it with your package manager of choice:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install with pip&lt;/span&gt;
pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;yaml-extras

&lt;span class=&quot;c&quot;&gt;# Install with poetry&lt;/span&gt;
poetry add yaml-extras
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you read this article and are inspired to make something with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml-extras&lt;/code&gt;, I’d love to hear
about it! (Shoot me an email at &lt;a href=&quot;mailto:dsillman2000@gmail.com&quot;&gt;dsillman2000@gmail.com&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Since the final article in the series, I’ve made significant refactors to some of the content I
shared which I will briefly summarize here.&lt;/p&gt;

&lt;h2 id=&quot;importanchor-and-import-allanchor&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import.anchor&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all.anchor&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;In the final article, I alluded to the possibility of a follow-up article that discussed “targeted”
imports, where you could specify a particular anchor in an external YAML file which you wanted to
import. I’ve since implemented this feature in the package, and it’s available for use, but the
implementation was somewhat nuanced.&lt;/p&gt;

&lt;p&gt;The challenge of implementing an “anchor-fetching” feature as just another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor&lt;/code&gt; alongside
my other custom constructors was that, by the time a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml.Node&lt;/code&gt; reaches the construction phase of
loading, all anchors and aliases have been resolved, so anchors are nowhere to be seen. To identify
subtrees of the AST which are marked with a particular anchor, I needed to intercept the AST before
it reached construction, meaning I needed to use the PyYAML Parser directly.&lt;/p&gt;

&lt;p&gt;As a refresher, the YAML Parser is responsible for taking the raw YAML Tokens which are &lt;em&gt;scanned&lt;/em&gt; 
from a file and producing YAML &lt;em&gt;Events.&lt;/em&gt; The events are subsequently &lt;em&gt;parsed&lt;/em&gt; into an AST of YAML 
&lt;em&gt;Nodes&lt;/em&gt; which finally get &lt;em&gt;constructed&lt;/em&gt; into Python objects. The last medium in this process which
has any notion of anchors are the YAML Events. So, in abbreviated pseudo-python code, here’s how I 
implemented a helper function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load_yaml_anchor(file_stream, anchor, loader_type)&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load_yaml_anchor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;anchor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# &quot;indent&quot; level tracker as we&apos;re traversing events to fully consume the anchored subtree
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Receptacle for the Events which compose the anchored subtree
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Commence parsing
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Case 1: we&apos;re on the anchored Scalar
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is a Scalar&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event has anchor == `anchor`&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Case 2: we&apos;re entering the anchored Mapping or Sequence
&lt;/span&gt;        &lt;span class=&quot;nf&quot;&gt;elif &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is start of a Mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; 
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is start of a Sequence&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event has anchor == `anchor`&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Case 3: we&apos;re in the anchored subtree
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Case 3a: we&apos;re entering a nested Mapping or Sequence
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is start of a Mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is start of a Sequence&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Case 3b: we&apos;re exiting a nested Mapping or Sequence
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is end of a Mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event is end of a Sequence&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Case 3c: we&apos;re exiting the anchored subtree
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It may seem pretty inductive and straightforward when you consider that there are only a few possible
structures that a single anchored node can take in a YAML file. But it took a good amount of trial
and error against a variety of unit test cases to settle on this design.&lt;/p&gt;

&lt;p&gt;If you’d like more in-depth information about how this helper function is used in the package to
support the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import.anchor&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all.anchor&lt;/code&gt; tags, I encourage you to check out the source
code on Github.&lt;/p&gt;

&lt;h2 id=&quot;import-all-parameterized&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all-parameterized&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Something that I didn’t get around to mentioning was a feature that I consider very important to
the usability of the package: parameterized imports (though this may seem like a convoluted and
unnecessary feature). The idea is that you can specify a glob pattern with one or more wildcards
in it (like with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt;), but you can attach to some of those glob patterns string names
which are then merged into the resulting Python objects parsed from each file.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
For instance, imagine we had the following directory structure:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;recipes/
    - Gingerbread.yml
    - ChocolateChip.yml
    - Sugar.yml
    - Oatmeal.yml
    - PeanutButter.yml
    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
We could imagine each of these YAML files have a similar structure,&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;ingredients&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ingredient &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ingredient &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;instructions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;instruction &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;instruction &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the ease of the developer (slash baker), we can assume that they would like to be able to add 
new recipes to the directory without needing to touch any other configs. There might be an outer 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recipes.yml&lt;/code&gt; file where we want to import each of the recipes into a sequence of mappings of their 
contents.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# recipes.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;recipes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Gingerbread&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/Gingerbread.yml&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Chocolate Chip&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/ChocolateChip.yml&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Sugar&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/Sugar.yml&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Oatmeal&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/Oatmeal.yml&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Peanut Butter&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/PeanutButter.yml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Each recipe looks like&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# - name: ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   ingredients:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   instructions:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simply won’t work because of how unwieldly it is and that it breaks our core requirement that
we should be able to add new recipes without adding more YAML entries to an ever-expanding index. 
Likewise, if we tried to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt;, we’d end up with a sequence of recipes which don’t have 
names!&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# recipes.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;recipes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import-all&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/*.yml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Each recipe looks like&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# - ingredients:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   instructions:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
So we could use this specialized tag, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import-all-parameterized&lt;/code&gt;, to ensure that we capture the name
of each file as a dedicated field in each resulting Python object:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# recipes.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;recipes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import-all-parameterized&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/{name:*}.yml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Each recipe looks like&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# - name: Gingerbread&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   ingredients:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   instructions:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a powerful feature for managing modular YAML files in a project. It allows us to outsource
some of the hierarchical categories of our YAML documents to the filesystem, which can also make
navigating the project’s definitions a little easier.
&lt;br /&gt;
In addition to simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; wildcards which can store a single component of the path, entire sub-paths
can also be stored as a parameter:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# recipes.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;recipes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import-all-parameterized&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;recipes/{category_tree:**}/{name:*}.yml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Each recipe looks like&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# - category_tree: baking/cookies&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   name: Gingerbread&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   ingredients:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   instructions:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     - ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;customizing-local-import-behavior&quot;&gt;Customizing local import behavior&lt;/h2&gt;

&lt;p&gt;As a final touch to make the package usable in downstream applications, I needed to finally
deprecate my assumption that the imports should always be seeking from the Python process’s working
directory.&lt;/p&gt;

&lt;p&gt;I ultimately implemented this by maintaining a global variable which was used to retrieve the
root of the filesystem from which all imports should be resolved. This variable could be get/set by
calling a function in the corresponding module of the package:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_extras&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_import&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Get the current import root
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_relative_import_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set the import root to my home
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml_import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_relative_import_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
This was a simple solution to a problem that I had been putting off for a while, and I’m glad to
have finally implemented it in a way which wasn’t too nasty.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I’m very proud of the work I’ve done on this package, and I’m excited to see if other people see
the same potential in it that I do for simplifying certain repetitive and template-worthy tasks
in their projects. I’m also excited to see if I can continue to improve the package and make it
more user-friendly and suitable for additional use cases.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.org/project/yaml-extras/&quot;&gt;yaml-extras on PyPI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dsillman2000/yaml-extras&quot;&gt;yaml-extras on Github&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://yaml-extras.pages.dev&quot;&gt;yaml-extras documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Modular YAML III: importing globs with &quot;import-all&quot;</title>
   <link href="https://www.dsillman.com/2024/11/26/yamlextras-iii/"/>
   <updated>2024-11-26T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2024/11/26/yamlextras-iii</id>
   <content type="html">&lt;p&gt;What happens when we want PyYAML to dynamically load an entire directory of YAML files into the contents of a single YAML document?
At the moment, this is painfully manual, where importing all the contents of a directory require separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; nodes for each
document:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items/item1.yml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items/item2.yml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items/item3.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
In this article, I propose a new dedicated tag with its own constructor for handling this use case, called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;handling-globs-with-import-all&quot;&gt;Handling globs with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;Let’s begin by looking at the dataclass we’ll use to represent an instance of a tagged node, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportAllSpec&lt;/code&gt;. Typical Python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;
objects won’t gel nicely with including Unix-style wildcards in the path, so we’ll start by creating a custom class for handling
path globs.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PathPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ll want a function on this type for yielding the list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;s captured by the glob pattern, which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pathlib&lt;/code&gt; conveniently includes.
For the sake of efficiency, we’ll memoize this function so that we never need to search the filesystem for a particular glob
pattern more than once. To hash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; in the memoized lookup dictionary, we’ll need to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PathPattern&lt;/code&gt; hashable. We can do this
by just inheriting the hash of the glob pattern itself, which we also include with our implementation.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;functools&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lru_cache&lt;/span&gt;
&lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PathPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__hash__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@lru_cache&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;glob_results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
Great, now we have a utility type for handling the glob pattern in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportAllSpec&lt;/code&gt;. Let’s implement that now:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportAllSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path_pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PathPattern&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ImportAllSpec&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PathPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
This will be the container class we use when we encounter a node tagged with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt;. Now, let’s implement the constructor
for this tag:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportAllConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Handle a node tagged with !import-all
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;import_all_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportAllSpec&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_scalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;import_all_spec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportAllSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import-all Expected a string, got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import-all Expected a string scalar, got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import_all_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import_all_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportAllSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Load the contents of all the files in the glob pattern
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import_all_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path_pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob_results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
Finally, we need to make sure we include the entry for the constructor in our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportLoader&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SafeLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import-all&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportAllConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
That turned out to be pretty easy! Now we can load a directory like that in the example at the start of this article with a single
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; tag:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import-all&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items/*.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
When we load a file like this with our custom loader, the contents of all the files in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;items&lt;/code&gt; directory will be loaded into
the Python object, e.g. it might look like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item1&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item3&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
Adding a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item4.yml&lt;/code&gt; file to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;items&lt;/code&gt; directory can now be loaded into the Python object without any changes to the YAML or
the underlying Python code performing the load. This is a powerful feature for managing modular YAML files in a project.&lt;/p&gt;

&lt;h2 id=&quot;what-about-merging-an-import-all-node&quot;&gt;What about merging an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; node?&lt;/h2&gt;

&lt;p&gt;But there’s one missing piece, as there was before in article I. We can’t use this feature with a merge key! I.e., we can’t do this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;merged-all&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import-all&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items_to_merge/*.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
If we had a directory of files like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A.yml&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A: 1&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B.yml&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B: 2&lt;/code&gt;, etc, we’d want to be able to load them all into a
single mapping, like&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;merged-all&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we had a directory full of YAML files which we wanted to merge into a single object (not a sequence of mappings), then we’d want
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; to resolve to a sequence of mappings, which are subsequently merged. Luckily, we can just update our existing override
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; to account for this case. In the event that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; merge key flattening method encounters a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; &lt;em&gt;scalar&lt;/em&gt;
node, we’ll just have it load each of the contents of the files in the glob pattern into a list of their respective Python objects, then
re-represent the list as a YAML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SequenceNode&lt;/code&gt;. The good news is that all of this actually fits into the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScalarNode&lt;/code&gt; branch of our
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method. Here’s the only change we need to make to the method:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SafeDumper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;represent_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import-all&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SafeDumper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;represent_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Perfect - now we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; tag with the merge key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; to merge the contents of a directory of YAML files into a single
mapping, like the example above. That example can now be successfully loaded as:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;merged-all&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s it! We’ve now got a powerful tool for managing modular YAML files in a project, utilizing the directory structure to maintain
hierarchical relationships between YAML modules. In the next article, I’ll talk about a way that we can improve this importing system by
allowing targeted imports of specific parts of other YAML files.&lt;/p&gt;

&lt;h2 id=&quot;safety-note&quot;&gt;Safety note&lt;/h2&gt;

&lt;p&gt;One other thing to mention is that nothing in this article prevents a user from creating a cyclical import, e.g., a pair of files&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# A.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;contents-of-B&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;B.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# B.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;contents-of-A&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;A.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will cause an infinite loop when loading the files which bubbles up to a “max recursion depth exceeded” error. This is a limitation
of the current implementation which I may consider addressing in a future article. For now, just be aware of this limitation when using
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import-all&lt;/code&gt; tags in your projects.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Modular YAML II: merging imports</title>
   <link href="https://www.dsillman.com/2024/11/25/yamlextras-ii/"/>
   <updated>2024-11-25T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2024/11/25/yamlextras-ii</id>
   <content type="html">&lt;p&gt;Where we last left off, I was just able to import a whole YAML file’s contents into another YAML file using a custom
Loader and Constructor in PyYAML for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag. But there’s a missing piece. What happens if I want to merge
multiple YAML files during import? First, let’s talk about the YAML merge key, “&amp;lt;&amp;lt;”, and how it works.&lt;/p&gt;

&lt;h2 id=&quot;the-yaml-merge-key&quot;&gt;The YAML merge key&lt;/h2&gt;

&lt;p&gt;The YAML merge key is a special key which allows you to merge the contents of one mapping into another. It’s denoted by
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; symbol. Here’s an example:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;mapping-a&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;&amp;amp;A&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key-a&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value-a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key-b&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value-b&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;mapping-b&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;&amp;amp;B&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key-b&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value-Bee&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key-c&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value-Cee&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;merged-mapping&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;*A&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;*B&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
If we were to look into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merged-mapping&lt;/code&gt; key after loading with PyYAML, we would see:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-a&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-a&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-Bee&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-Cee&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
An alternative syntax for using the merge key is to define &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; as a repeated key in the mapping, e.g.,&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;merged-mapping&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;*A&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;*B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
So let’s try to use our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportLoader&lt;/code&gt; to load a YAML file which merges the contents of multiple YAML files. Suppose
we have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A.yml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B.yml&lt;/code&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key-*&lt;/code&gt; mappings above defined in them. Then we can test our loader with the
below example file:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# merged.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;A.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;B.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
When attempting to load the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merged.yml&lt;/code&gt; file, we get this runtime error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
yaml.constructor.ConstructorError: while constructing a mapping
...
expected a mapping or list of mappings for merging, but found scalar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-does-yaml-merging-work&quot;&gt;How does YAML merging work?&lt;/h2&gt;

&lt;p&gt;Digging deeper into the stack trace, it becomes clear that the error is cropping up during the merge key handling,
which is initiated in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Loader&lt;/code&gt; class using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor.construct_mapping&lt;/code&gt; method. This method is responsible
for constructing any and all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MappingNode&lt;/code&gt;s which are encountered during the composition of the YAML document. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;construct_mapping&lt;/code&gt; method, in turn, calls the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor.flatten_mapping&lt;/code&gt; method. As compared to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;construct_mapping&lt;/code&gt;
method, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method actually handles the merge key logic. Check out its implementation in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafeConstructor&lt;/code&gt; class in the PyYAML source code:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:yaml.org,2002:merge&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MappingNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SequenceNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;submerge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MappingNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConstructorError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;while constructing a mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_mark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;expected a mapping for merging, but found %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
                                &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_mark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;submerge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;submerge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submerge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConstructorError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;while constructing a mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_mark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;expected a mapping or list of mappings for merging, but found %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_mark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:yaml.org,2002:value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:yaml.org,2002:str&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The operative condition in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if key_node.tag == &apos;tag:yaml.org,2002:merge&apos;:&lt;/code&gt; check.
When a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; key is encountered, its value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; is expected to be either a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MappingNode&lt;/code&gt; or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SequenceNode&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MappingNode&lt;/code&gt;s.
If this method encounters a scalar value, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import A.yml&lt;/code&gt;, we land in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;else&lt;/code&gt; block, which raises the error we saw
above. The crux of the issue is that we want our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag to handle the loading of the file before the result is passed
to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method.&lt;/p&gt;

&lt;h2 id=&quot;handling-the-merge-key-in-our-custom-loader&quot;&gt;Handling the merge key in our custom Loader&lt;/h2&gt;

&lt;p&gt;The problem is that we can’t implement this behavior in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportConstructor&lt;/code&gt; class. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportConstructor&lt;/code&gt; class is
only invoked when a node is encountered with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag during &lt;em&gt;construction.&lt;/em&gt; This error is cropping up just before we
can get around to constructing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; node. So a work-around I thought of is to override the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method in
our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportLoader&lt;/code&gt; class such that we can intercept the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag before it gets passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method,
load the file, and then pass the loaded data to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method. Here’s the implementation:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringIO&lt;/span&gt;
&lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SafeLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MappingNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:yaml.org,2002:merge&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SafeDumper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;represent_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that I’ve pretty much copied the internal logic of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; method from the PyYAML source code, except only
including a case for handling the scalar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt;-tagged node. We call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;construct_object&lt;/code&gt; on this node, which will identify the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag and call our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportConstructor&lt;/code&gt; to load the file.&lt;/p&gt;

&lt;p&gt;The problem is that, at this point, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imported_value&lt;/code&gt; is a Python object representing the contents of the file, but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt;
method expects a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; object. So that means we need to move back up the representation chain and convert the Python object back into a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; object. Just like PyYAML has a chain of abstractions (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(FileStream, Scanner)&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(Tokens, Parser)&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(Events, Composer)&lt;/code&gt; 
-&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(Nodes, Constructor)&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python Object&lt;/code&gt;) which converts a YAML file into a Python object, there is a reverse chain of abstractions
which converts a Python object back into a YAML file. For the purposes of this post, let’s just focus on the first step, which converts a Python
object back into an AST of YAML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; objects. This is performed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Representer&lt;/code&gt;, which can be accessed directly on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dumper&lt;/code&gt;
object using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dumper.represent_data&lt;/code&gt; method. Because instantiating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dumper&lt;/code&gt; requires some target to which the YAML output will be dumped,
I just instantiate an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringIO&lt;/code&gt; receptacle for this purpose. Finally, this allows me to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imported_repr&lt;/code&gt; object, which is an AST
of the contents of the imported YAML file. Now I can pass the result to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafeConstructor.flatten_mapping&lt;/code&gt; method, which will merge the
result into the document as-expected.
&lt;br /&gt;
&lt;br /&gt;
When we try our code now, we get the expected output:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;merged-mapping&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-a&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-a&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-Bee&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;key-c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value-Cee&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a pretty good solution, but it’s not completely done. As we saw in the core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafeConstructor.flatten_mapping&lt;/code&gt; method, there’s another way
you can use the merge key, which is to have a sequence of mappings to merge, like we saw in the above example of merge key usage:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;merged-mapping&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;*A&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;*B&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;handling-sequences-of-import-nodes&quot;&gt;Handling sequences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; nodes&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Can we modify our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportLoader.flatten_mapping&lt;/code&gt; method to handle this case as well for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; nodes? E.g.,&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;merged-mapping&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A.yml&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B.yml&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course we can! It’s just a matter of adding an additional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; block to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten_mapping&lt;/code&gt; override to handle the case where the value is
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SequenceNode&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScalarNode&lt;/code&gt;s tagged with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt;. Here’s the implementation:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SafeLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MappingNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
      &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:yaml.org,2002:merge&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SequenceNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SafeDumper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;represent_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                              &lt;span class=&quot;n&quot;&gt;imported_value&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imported_repr&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten_mapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that I’m following the exact same order-of-operations as the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafeConstructor.flatten_mapping&lt;/code&gt; implementation
for sequences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MappingNode&lt;/code&gt;s. When I recognize that the value of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; key is a sequence, I iterate over the sequence
values. When I encounter an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; node, I perform the same steps as before to load the file and convert the Python object
back into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; object. Finally, to match the implementation of the merge key for sequences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MappingNode&lt;/code&gt;s, I reverse the
sequence of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; objects and replace the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SequenceNode&lt;/code&gt; with the new sequence of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; objects. This reversal ensures
that the precedence of merged mappings is maintained, i.e. the last mapping in the sequence takes precedence over the first mapping.&lt;/p&gt;

&lt;p&gt;And that’s it! We now have a fully-functional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag which can be used to import YAML files into other YAML files, &lt;em&gt;plus&lt;/em&gt; it
can handle merging multiple imports into a single mapping using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt; YAML merge key. It can even handle a mix of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; and
non-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; mappings in the same merge:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;override-config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;&amp;amp;override&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;localhost&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;connection_profile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lab&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default-config.yml&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;*override&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The functionality we’ve implemented here allows us to create modular YAML files which can be imported and merged into other YAML files.
But there’s more to be done to make this sufficiently general and robust. In the next post, I’ll talk about a variant we can implement
to handle the case where we want to import an entire glob pattern of YAML files as a sequence of Python objects.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Modular YAML I: a custom &quot;import&quot; tag</title>
   <link href="https://www.dsillman.com/2024/11/22/yamlextras/"/>
   <updated>2024-11-22T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2024/11/22/yamlextras</id>
   <content type="html">&lt;p&gt;Since I’ve started working at Intuitive full-time, I’ve been interacting with YAML files in some capacity pretty much 
every day. Some of that is because I do a lot of &lt;a href=&quot;https://www.getdbt.com/&quot;&gt;dbt&lt;/a&gt; development as a data engineer, and dbt 
uses YAML files to define models, tests and other properties about the data model. And there’s pre-commit, Gitlab CI, as
well as other custom Intuitive tools that use YAML files to define configurations.&lt;/p&gt;

&lt;p&gt;This frequent interaction with YAML has led me to also write some of my own tools and scripts which depend upon source
data which is laid out in YAML files. One such project I’ve written with these tools is a Python CLI which reads a 
directory of YAML files and Jinja templates and outputs a directory of rendered files. The original purpose of the 
kernel of this project was for mass-producing Jira content and standardizing other documentation. I’ve also used it to
mass-generate unit tests which are so similar to one another that they can be effectively templated.&lt;/p&gt;

&lt;p&gt;Something that most of my Python code would be preoccupied with was crawling the local directories for YAML files and
associating them with the right Jinja templates in a way that scaled well, was flexible and DRY. I’m still working on 
this broader project of general DRY templated document generation to this day.&lt;/p&gt;

&lt;h2 id=&quot;what-are-yaml-tags&quot;&gt;What are YAML tags?&lt;/h2&gt;

&lt;p&gt;YAML tags are a way to extend the YAML specification to include custom data types. The YAML specification includes a
number of built-in tags, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!str&lt;/code&gt; for strings, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!int&lt;/code&gt; for integers, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!float&lt;/code&gt; for floats, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!bool&lt;/code&gt; for booleans.
Syntactically, they only need one leading exclamation mark, but most of the built-in tags have two.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Example of YAML tags&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!!str&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Hello,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;world!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, they’re sort of like anchors in how they just immediately precede a YAML node. Except that they’re more 
like type hints, or general-purpose annotations. They’re not mutually exclusive - the syntax to tag and anchor a YAML 
node is as follows:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Tagging and anchoring a YAML node&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;tagged-and-anchored&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!my-tag&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;&amp;amp;my-anchor&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unlike anchors, tags can be used as many times as you want (anchors need to be unique for the YAML resolver to properly
resolve them). Tags can also be used to define custom data types, which is why sometimes you’ll see them if you dump
a YAML representation of a custom class from Python.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Example of a custom data type&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;!MyCustomType&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;custom_instance&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;my_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!!str&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;value&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So if I wanted to “import” a YAML file from another YAML file, it would be syntactically sound to define a custom tag
for this purpose.&lt;/p&gt;

&lt;h2 id=&quot;envisioning-import&quot;&gt;Envisioning “!import”&lt;/h2&gt;

&lt;p&gt;One part of the puzzle which might get me closer to being able to flexibly managing YAML files in a DRY way that scales
well is to use a modular approach. If I’m able to organize documents in a directory tree to represent hierarchical
relationships between documents, I can effectively split responsibilities between documents. The core mechanism that
would allow this functionality is some way to reference the contents of YAML file \(X\) from within a YAML file \(Y\).
&lt;br /&gt;
&lt;br /&gt;
I’d imagine that this operation looks something like this:
\&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# X.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Y.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
If we were to suppose that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Y.yml&lt;/code&gt; contained the following:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Y.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Hello,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;world!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
Then we’d want to be able to read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X.yml&lt;/code&gt; into memory as the following Python dictionary:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Hello, world!&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;lets-talk-about-pyyaml&quot;&gt;Let’s talk about PyYAML&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://pyyaml.org/&quot;&gt;PyYAML&lt;/a&gt; is by far the most popular and most widely-used YAML parser for Python. It’s a wrapper 
around the C library &lt;a href=&quot;https://pyyaml.org/wiki/LibYAML&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libyaml&lt;/code&gt;&lt;/a&gt;, and it’s the parser that’s used by dbt to parse YAML
configurations.&lt;/p&gt;

&lt;p&gt;When Googling to see if anyone’s solved my “YAML importing” problem yet, I was not disappointed by the excellent design 
of &lt;a href=&quot;https://github.com/tanbro/pyyaml-include&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyyaml-include&lt;/code&gt;&lt;/a&gt;. Its design of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!include&lt;/code&gt; tag was a great 
inspiration for me to figure out how to implement my own tagging system which followed different conventions to serve 
different purposes.&lt;/p&gt;

&lt;p&gt;Reading the source code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyyaml-include&lt;/code&gt; introduced me to the pattern of using a custom &lt;em&gt;constructor&lt;/em&gt; to parse a
specialized tag. This is the pattern I would use to implement my own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;PyYAML loads data from a YAML file into a Python object using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Loader&lt;/code&gt; object. The process of loading a YAML file starts
with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scanner&lt;/code&gt;, which reads the file character by character and tokenizes it into a stream of tokens. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parser&lt;/code&gt;
then takes these tokens to build a sequence of parsed events. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Composer&lt;/code&gt; then takes these events and builds an
abstract syntax tree (AST) of nodes. Finally, specialized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor&lt;/code&gt;s take each node of the AST to build a complete 
Python object. All the way through this process, tags are propagated as fields of the events and nodes. However, when 
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor&lt;/code&gt; is being chosen for a node, the tag on the node will determine which constructor is used.&lt;/p&gt;

&lt;h2 id=&quot;extending-pyyaml&quot;&gt;Extending PyYAML&lt;/h2&gt;

&lt;p&gt;If we want to be able to parse our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag, we’ll need to create our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constructor&lt;/code&gt; class which gets
called to handle nodes which are encountered with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag. For good measure, we’ll also create a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Loader&lt;/code&gt;
class to ensure that our custom constructor is used with this specific tag.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataclasses&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataclass&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ImportSpec&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Handle a node tagged with !import
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;import_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportSpec&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;construct_scalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;import_spec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import Expected a string, got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import Expected a string scalar, got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Just load the contents of the file
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;import_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SafeLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!import&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImportConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That may seem like a lot of code, but it’s actually pretty simple. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportSpec&lt;/code&gt; class is a simple dataclass which
wraps a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pathlib.Path&lt;/code&gt; object and provides a method to construct an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportSpec&lt;/code&gt; from a string. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportConstructor&lt;/code&gt;
class is a callable class which takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml.Loader&lt;/code&gt; object and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml.Node&lt;/code&gt; object and returns the contents of the
YAML file specified by the scalar string in the node. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportLoader&lt;/code&gt; class is a subclass of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml.SafeLoader&lt;/code&gt; which
adds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag to the constructor map with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportConstructor&lt;/code&gt; object.&lt;/p&gt;

&lt;h2 id=&quot;using-our-custom-tag&quot;&gt;Using our custom tag&lt;/h2&gt;

&lt;p&gt;Now we can load a YAML file with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag and have the contents of the file loaded into the Python object. Here’s
an example of how we might use this in practice:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_import&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportLoader&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;X_yml&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;X.yml&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;X_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_yml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImportLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which yields,&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Hello, world!&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nice! Now we can import YAML files from other YAML files. There’s more to be done, though. In the next post, I’ll talk
about making this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!import&lt;/code&gt; tag work with the YAML merge key “&amp;lt;&amp;lt;”. Stay tuned!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Linear Regression in Rust with AutoGrad</title>
   <link href="https://www.dsillman.com/2022/05/05/rustautograd/"/>
   <updated>2022-05-05T00:00:00+00:00</updated>
   <id>https://www.dsillman.com/2022/05/05/rustautograd</id>
   <content type="html">&lt;p&gt;I’ve spent the past two weeks just starting to get a handle
on how to use the popular low-level, memory-safe programming language Rust. In general,
it gives me C++ vibes when I’m programming in it. That said, I think that it could have
good potential as a machine learning development environment, so long as it is
supplied with the appropriate external libraries (or “crates,” to use Rust’s
terminology.)&lt;/p&gt;

&lt;p&gt;So, to get a handle on how I could use Rust specifically
for machine learning purposes, I did some (very brief) research into what tools
might be useful. I knew that I wanted to make use of an &lt;em&gt;auto-differentiation&lt;/em&gt;
tool, as this is essentially the bedrock of a machine learning dev environment.
Auto-differentiation allows us to compute partial derivatives of the output of
our models such that we can tweak and tune them into being better predictors. I looked
over three different auto-differentiation crates:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/easy-ml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;easy-ml&lt;/code&gt;&lt;/a&gt; : This is actually a much more comprehensive
machine learning crate with lots of out-of-the-box machine learning implementations
which are easy to implement. It also contains a module,
&lt;a href=&quot;https://docs.rs/easy-ml/1.8.1/easy_ml/differentiation/index.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;differentiation&lt;/code&gt;&lt;/a&gt;
which houses its auto-differentiation engine. This module could suffice for my purposes,
but I don’t like the overhead of needing to install all of the extra bells and whistles
provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;easy-ml&lt;/code&gt; just to get access to its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;differentiation&lt;/code&gt; module. I’d rather
use a crate which contains only the necessary tools &amp;amp; traits for auto-differentiation
in particular.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/autodiff&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autodiff&lt;/code&gt;&lt;/a&gt; : The problem with using this crate
in particular is that it is mostly tailored toward calculus on scalar functions,
whereas most machine learning applications require higher-dimensional differentiation
in the form of vectors, matrices and scalars. I don’t know if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autodiff&lt;/code&gt; is necessarily
incapable of implementing these features, but most of their examples and documentation
emphasize their scalar equivalents.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/autograd&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;&lt;/a&gt; : Of the above two alternatives,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt; certainly takes the cake for popularity and satisfies my complaints
about the other two. The crate implements auto-differentiation  on the standard
Rust matrix/tensor crate, &lt;a href=&quot;https://crates.io/crates/ndarray&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ndarray&lt;/code&gt;&lt;/a&gt;, which can
allow for maximal cross-compatibility with other matrix-related crates. Moreover, it
is very well documented and small enough for me to be able to scan the source code
to answer questions not addressed by the documentation. Moreover, it’s easily extendible
for custom tensor operations and I/O pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, I decided to go with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt; for my auto-differentiation
engine. I could have gone deeper into my research to find other alternatives, but
these three appeared to be the most popular (according to crates.io), and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;
serves all of the basic purposes which I am looking for.&lt;/p&gt;

&lt;h3 id=&quot;linear-regression-as-a-neural-network&quot;&gt;Linear Regression as a Neural Network&lt;/h3&gt;

&lt;p&gt;The simplest machine learning application I could come
up with to test this crate out (which wasn’t already done in their tutorials collection)
was a simple linear regression neural network. For those who don’t know, a neural
network is essentially a statistical model which recursively applies a linear regression
and some nonlinear &lt;em&gt;activation&lt;/em&gt; function. So, if I wanted to use a neural network
for the purposes of a simple, one-dimensional linear regression, I would use a 1D
input layer connected directly to a 1D output layer with no activation function.
The weight parameter of the single neuron in this network corresponds to the slope
of our linear regression, while its bias parameter is the offset. This is why the
output equation of our model would look exactly like the formula for a line:&lt;/p&gt;

&lt;p class=&quot;text-responsive&quot;&gt;
$$
y = mx + b \qquad \Leftrightarrow \qquad y = \text{weight}\cdot x + \text{bias}
$$
&lt;/p&gt;

&lt;p&gt;In a diagram, this linear regression model looks like the
neural network pictured below:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;centered-image&quot; alt=&quot;1D Linear Reg Picture&quot; src=&quot;/assets/images/1dlinregmodel.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Obviously, if this was all I wanted to use my auto-differentiation
engine for, it would be woefully overpowered, but this is just a simple “hello world.”
More generally, if I really wanted (only) to implement a linear regression algorithm
in Rust, I would just use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Linear_regression#Least-squares_estimation_and_related_techniques&quot;&gt;closed-form formula for a linear regression&lt;/a&gt; and use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ndarray-linalg&lt;/code&gt; for matrix multiplications and inverses to implement it. However,
in future, I’d like to construct more complicated neural models which will require the
full power of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;, so this is a small proof-of-concept which allows me to get
comfortable with the crate.&lt;/p&gt;

&lt;p&gt;Now that I have a basic idea of what sort of model I’m
going to be building, I can start writing some Rust code.&lt;/p&gt;

&lt;h3 id=&quot;implementing-the-model-with-autograd&quot;&gt;Implementing the Model with AutoGrad&lt;/h3&gt;

&lt;p&gt;The main example from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;’s GitHub repo with which
my project has the most overlap is the &lt;a href=&quot;https://github.com/raskr/rust-autograd/blob/master/examples/sine.rs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sine.rs&lt;/code&gt;&lt;/a&gt;
example, which regresses a multi-layer perceptron (MLP) to the sine function.
Because I’m doing something simpler, I’ll only be following it insofar as it is a
useful reference for how regression works generally in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;.
&lt;br /&gt;
&lt;br /&gt;
The first thing I do is import a handful of modules from the crate:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;autograd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;optimizers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;optimizers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;adam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Adam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;prelude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;tensors_ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first of these just assigns a nickname, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ag&lt;/code&gt;, which I can use in place of the
longer namespace, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;. After that, I’m importing tools for optimizers, the
Adam optimizer specifically, some high-level traits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;’s prelude, and
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tensor_ops&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;Next, I create my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; function, and start initializing the core engine of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;:
the so-called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VariableEnvironment&lt;/code&gt;, along with an instance of a random number generator.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Initialize VariableEnvironment and (default seed) RNG&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;VariableEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rng&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ndarray_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ArrayRng&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In short, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VariableEnvironment&lt;/code&gt; is a container which
manages all of the variables which we use in learning and keeps track of how they
change throughout the learning process. In setting it up, I register our two learned
parameters in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VariableEnvironment&lt;/code&gt;, which involves associating them with a name:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Initializing our parameter tensors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rng&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.standard_uniform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ndarray_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env.name()&lt;/code&gt;, we are registering a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NamedVariableSlot&lt;/code&gt; in the environment,
which we then immediately give an initial value with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set()&lt;/code&gt; method. The arguments
of the functions inside of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt; call are each references to arrays of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usize&lt;/code&gt;,
each of which state that we are initializing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt; with a \(1\times 1\) tensor of
random values, and that we are initializing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; with a \(1\times 1\) tensor of zeros.&lt;/p&gt;

&lt;p&gt;So, our next step in initializing the model is creating an
instance of an optimizer which governs how we use the partial derivatives we compute
in order to reduce loss and predict outputs more accurately. We’re using the Adam
optimizer for this, which we give some parameters to:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Initializing our optimizer (Adam)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Adam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;mf&quot;&gt;0.01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Learning rate (alpha)&lt;/span&gt;
  &lt;span class=&quot;mf&quot;&gt;1e-08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Error Toler. (epsilon)&lt;/span&gt;
  &lt;span class=&quot;mf&quot;&gt;0.9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// First Moment decay (beta1)&lt;/span&gt;
  &lt;span class=&quot;mf&quot;&gt;0.999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Second Moment decay (beta2)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.default_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.current_var_ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Variable IDs from &lt;/span&gt;
                                              &lt;span class=&quot;c1&quot;&gt;// VariableEnvironment namespace&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                                  &lt;span class=&quot;c1&quot;&gt;// VariableEnvironment instance    &lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;linear_reg_adam&quot;&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;// Name string&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All of the parameters I’ve put in above are generally considered
to be the “default” parameters for most use cases of Adam, but I’ve beefed up the
learning rate (\(\alpha\)) by a factor of 10 to speed up learning on such a dimensionally
small model. It’s also noteworthy that I need to provide the optimizer with the Variable
IDs from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VariableEnvironment&lt;/code&gt; (so it knows what our learned parameters are), as
well as a &lt;em&gt;mutable&lt;/em&gt; reference to the environment. We also supply it with a name.&lt;/p&gt;

&lt;p&gt;Now I start thinking about the training process. For the purposes
of this project, I’d like to train the linear regressor in batches of size \(64\), over the
course of \(1000\) epochs (i.e. batches). So I just quickly define some constants for
these quantities:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Initializing training constants&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N_EPOCHS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BATCHSIZE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, I’m almost ready to code up the training loop.
But it feels like I’m forgetting something; perhaps the actual training data itself?
Where am I getting that data from? Because we’re working totally with a synthetic dataset
for this project, it suffices to define some “true” values we expect our network to learn,
then use those values (and some data noise) to generate data points as we train. So,
I define the parameters I’d like to learn:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Setting up the (synthetic) dataset&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.5f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noise_scale&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
Now we can jump into the meat of our algorithm: the training
loop. This consists of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt;-loop to iterate over the epochs, along with the following
contents which I will explain:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N_EPOCHS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Randomly generate input coords&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;standard_uniform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BATCHSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;20.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;10.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Retrieve variables&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Compute expected output (w/ noise)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;standard_normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BATCHSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noise_scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Compute LinReg predictions&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;matmul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Compute Mean Sq Error Loss&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reduce_mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.default_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Compute gradients for variables&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;grad_helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Compute weight/bias change&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adam&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get_update_op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// &quot;Push&quot; both loss and changes to evaluation buffer&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.evaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Print training loss every 100 epochs&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;&quot;Mean Loss (epoch {}) = {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we note that we are passing a function to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VariableEnvironment&lt;/code&gt;
via the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt;. This function expects its argument to be a function with an argument
of a type called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt;, which is often abbreviated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctx&lt;/code&gt; in variable names,
which I use in the closure. Anything we run inside of the closure we pass to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env.run()&lt;/code&gt;
is used to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Graph&lt;/code&gt;, which is a component frequently seen in auto-differentiation
engines. Without getting into too many nitty-gritty details, you can think of this as a
graph where nodes are variables in our model, and we have arrows between them indicating
how data flows between them such that we can “trace back” how they interact with one another and
use this data to compute partial derivatives.&lt;/p&gt;

&lt;p&gt;So what does all of this mean for us? It means that anything we do
inside of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env.run()&lt;/code&gt; will be “watched” by the graph so we can compute derivatives. Let’s
take a look at what we do inside of the context closure, one digestible chunk at a time:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Randomly generate input coords&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;standard_uniform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BATCHSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;20.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;10.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Retrieve variables&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Compute expected output (w/ noise)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;standard_normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BATCHSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noise_scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Compute LinReg predictions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;matmul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What we initially do is generate a tensor of uniformly-distributed
input coordinates, spread evenly on the interval \((-10, 10)\). Then, I call the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variable()&lt;/code&gt; function from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt; instance, which allows me to retrieve a variable
tensor by the name I assigned to it earlier on when I initialized it. The next thing I do
is generate the expected output for these data points according to the line formula,
to which I add a (scaled) tensor of Gaussian noise to add some realistic data noise like
we would see in a real dataset.&lt;/p&gt;

&lt;p&gt;On the last line, I assign to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt; what’s normally called the
“forward propagation” of the network. That is, I take the tensor batch of input points and
propagate it through the model and compute the output which gives our model’s prediction.
This is done through a matrix multiplication (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;matmul()&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tensor_ops&lt;/code&gt; module)
after which we add our bias tensor variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Compute Mean Sq Error Loss&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reduce_mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.default_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Compute gradients for variables&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;grad_helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Compute weight/bias change&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adam&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get_update_op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;Push&quot; both loss and changes to evaluation buffer&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.evaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this next chunk, I start by computing the current loss
of the model on the current batch, storing the value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean_loss&lt;/code&gt;. Because I’m
using Mean Squared Error (MSE) as our loss metric, I can compute it very simply by
constructing a tensor of the difference between our prediction and expectation (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z - y&lt;/code&gt;),
squaring its elements (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;square()&lt;/code&gt;), then, taking the average of the resulting tensor
in the form of a scalar (accomplished by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce_mean&lt;/code&gt;). The extra arguments at the end of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce_mean&lt;/code&gt; are an array of the axes along which we’re taking the mean (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;[0]&lt;/code&gt;), and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to indicate that we want to discard the axis we’re taking the mean along, which
leaves us with a scalar.&lt;/p&gt;

&lt;p&gt;The next meaningful line is where we assign values to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vars&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grads&lt;/code&gt; - this uses the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grad_helper()&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optimizers&lt;/code&gt; module, to
which I hand both an array of the loss variable(s) I’m trying to minimize (in our case,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;[mean_loss]&lt;/code&gt;, the MSE variable), as well as the namespace from our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt; which
contains all of our learnable parameters (in our case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; as variable tensors).&lt;/p&gt;

&lt;p&gt;I then use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adam&lt;/code&gt; optimizer instance to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adam.get_update_op()&lt;/code&gt;,
which takes in references to the variables and their respective gradients, as well as the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt; instance, and returns the adjustments that need to be made to the variables
in order to reduce the loss, which I call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_op&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the next line, I call a sequence of functions in the form of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctx.evaluator().push().run()&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt; instance contains an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Evaluator&lt;/code&gt;, which
actually enacts the changes suggested by our optimizer. The pattern important here is
“pushing” values to the evaluator, then finally calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt; to turn those values
into instructions that get evaluated. In computer science, data structures that store
a set of instructions and only evaluate their output when it’s necessary is known as a
“lazy data structure.” This is why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt; is built upon a “Lazy Tensor” evaluation
engine - the results aren’t explicitly computed (which can be costly) &lt;em&gt;until&lt;/em&gt; we push
them to the evaluator and call “run.”&lt;/p&gt;

&lt;p&gt;I initially had to ask; “why are we pushing our mean loss?”
After all, that’s not necessary for any of the tensor updates. The main reason is so that
we can print out the &lt;em&gt;evaluated&lt;/em&gt; value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean_loss&lt;/code&gt;. Because we computed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean_loss&lt;/code&gt; purely
with tensor variables, the resulting scalar is still a tensor variable and doesn’t actually
contain a numeric value. Because I’d like to print out what error we have, I need to push
it to the evaluator and get that numeric value lazily. In the next line, I use a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; statement (which I like to think of as Rust’s equivalent of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; statement)
to print out the loss of the algorithm every 100 epochs.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Print training loss every 100 epochs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;Mean Loss (epoch {}) = {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the index I’m using to select from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results&lt;/code&gt; to get
the numeric value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean_loss&lt;/code&gt; corresponds to the order which we pushed symbolic
tensors to the evaluator. Because I pushed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean_loss&lt;/code&gt; first, it takes index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;. The
actual entry at that index is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt; type (which can be either an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ok&lt;/code&gt; value for the
numeric tensor value, or an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Err&lt;/code&gt; error value), so I need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unwrap()&lt;/code&gt; it.&lt;/p&gt;

&lt;p&gt;Now that we’ve coded up the training loop, the last thing there
is to do is handle behavior of this script after the training is complete. I’d like to
save the learned parameters to a file, which is luckily very easy with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;results.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// Save the learned parameters to a JSON file&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The very last thing I do before ending the script is print to
the command line what the learned parameters are after our last epoch, so we can
visually compare them to their “true” values we set for our synthetic dataset.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// --snip--&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.default_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Borrow the learned weight and bias values&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get_array_by_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.borrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get_array_by_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.borrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Print them out&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Final w = {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Final b = {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once again, I make use of the environment’s namespace, storing
it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ns&lt;/code&gt;. It is from this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Namespace&lt;/code&gt; instance which I can borrow the latest evaluated values
assigned to each variable by calling the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_array_by_name()&lt;/code&gt;, which also results
in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt;, so we must unwrap it. However, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt; itself wraps a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RefCell&lt;/code&gt; object
(used often in multi-threaded applications), from which we can get the stored value via
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;borrow()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;And that’s pretty much all there is to it! Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo build&lt;/code&gt;
gives us no compiler errors or warnings, and we can execute the resulting compiled
program to see the results we expected:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;centered-image&quot; alt=&quot;Cmd Line Results&quot; src=&quot;/assets/images/autograd_linreg_output.PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m satisfied with these results, because it’s clear that the
error quickly converged within the first 700 or so epochs, and our learned parameters
are within 0.05 of their “true values.”&lt;/p&gt;

&lt;h3 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here&lt;/h3&gt;

&lt;p&gt;I’d like to find more practical uses of the auto-differentiation utilities provided by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autograd&lt;/code&gt; in a more comprehensive machine learning project. I think the next application
I’ll try to implement with this will be a custom Variational Autoencoder (VAE). Stay
tuned for blog updates to see how I get on with implementing this (admittedly, much
more complicated) model in the future.&lt;/p&gt;
</content>
 </entry>
 

</feed>
