<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Chapter 02.11 — opinionated/ layer on Ocarina — manual</title>
    <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/</link>
    <description>Recent content in Chapter 02.11 — opinionated/ layer on Ocarina — manual</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Thu, 04 Jun 2026 17:04:59 +0200</lastBuildDate>
    <atom:link href="https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>02.11.01 — CliBuilder &#43; CliArg &#43; _SilentArgumentParser</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/01-cli-builder/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/01-cli-builder/</guid>
      <description>&lt;h1 id=&#34;021101clibuilder--cliarg--_silentargumentparser&#34;&gt;02.11.01 — &lt;code&gt;CliBuilder&lt;/code&gt; + &lt;code&gt;CliArg&lt;/code&gt; + &lt;code&gt;_SilentArgumentParser&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021101clibuilder--cliarg--_silentargumentparser&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/builder.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/builder.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Declarative overlay on top of &lt;code&gt;argparse&lt;/code&gt;. &lt;em&gt;Aggregates&lt;/em&gt; validation errors, &lt;em&gt;rewrites&lt;/em&gt; the help output on error, and &lt;em&gt;registers&lt;/em&gt; post-parse effects.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;_silentargumentparser&#34;&gt;&lt;code&gt;_SilentArgumentParser&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_silentargumentparser&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_SilentArgumentParser&lt;/span&gt;(ArgumentParser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, message: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Never:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Raise an error.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(message)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The standard &lt;code&gt;ArgumentParser&lt;/code&gt; calls &lt;code&gt;sys.exit(2)&lt;/code&gt; directly on error. With that turd of a default, you &lt;strong&gt;can&amp;rsquo;t&lt;/strong&gt; intercept and you &lt;strong&gt;can&amp;rsquo;t&lt;/strong&gt; aggregate multiple errors.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;_SilentArgumentParser&lt;/code&gt; &lt;em&gt;reroutes&lt;/em&gt; errors as &lt;code&gt;ValueError&lt;/code&gt;. So &lt;code&gt;CliBuilder.parse&lt;/code&gt; can catch and aggregate them.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.02 — CliStore[TKeys] &#43; _CliField[T] &#43; phantom_validate</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/02-cli-store-phantoms/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/02-cli-store-phantoms/</guid>
      <description>&lt;h1 id=&#34;021102clistoretkeys--_clifieldt--phantom_validate&#34;&gt;02.11.02 — &lt;code&gt;CliStore[TKeys]&lt;/code&gt; + &lt;code&gt;_CliField[T]&lt;/code&gt; + &lt;code&gt;phantom_validate&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021102clistoretkeys--_clifieldt--phantom_validate&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source files: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/store.py&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/phantoms.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/phantoms.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Write-once&lt;/strong&gt; store for parsed CLI values. Each field validates on write. &lt;code&gt;TKeys: Literal[...]&lt;/code&gt; gives key autocomplete.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;_clifieldtwrite-once-field&#34;&gt;&lt;code&gt;_CliField[T]&lt;/code&gt; — write-once field&lt;a class=&#34;anchor&#34; href=&#34;#_clifieldtwrite-once-field&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_CliField&lt;/span&gt;[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, validate: ValidationChainBuilder[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value: T &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; _Unset &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _UNSET&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_validate &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; validate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, value: T) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value, _Unset):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Value already set.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_validate(_validate(value))&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; T:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value, _Unset):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Value not set yet.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;1-_unset-sentinel&#34;&gt;1. &lt;code&gt;_Unset&lt;/code&gt; sentinel&lt;a class=&#34;anchor&#34; href=&#34;#1-_unset-sentinel&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_Unset&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_UNSET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _Unset()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why not &lt;code&gt;None&lt;/code&gt;? Because &lt;code&gt;None&lt;/code&gt; can be a &lt;strong&gt;legitimate value&lt;/strong&gt; (&lt;code&gt;--profile-path&lt;/code&gt; isn&amp;rsquo;t mandatory; its post-parse value can legitimately be &lt;code&gt;None&lt;/code&gt;). A dedicated sentinel separates &amp;ldquo;&lt;em&gt;not set yet&lt;/em&gt;&amp;rdquo; from &amp;ldquo;&lt;em&gt;set to None&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.03 — Auto CLI store &#43; flags &#43; validation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/03-selenium-cli/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/03-selenium-cli/</guid>
      <description>&lt;h1 id=&#34;021103auto-cli-store--flags--validation&#34;&gt;02.11.03 — Auto CLI store + flags + validation&lt;a class=&#34;anchor&#34; href=&#34;#021103auto-cli-store--flags--validation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/selenium/create_cli_store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/selenium/create_cli_store.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;flags&#34;&gt;Flags&lt;a class=&#34;anchor&#34; href=&#34;#flags&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Flag&lt;/th&gt;&#xA;          &lt;th&gt;Default&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Validation&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--driver-path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_file&lt;/code&gt; (except &lt;code&gt;--browser safari&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--profile-path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_none&lt;/code&gt; OR &lt;code&gt;is_dir&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--browser&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; (required)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;argparse &lt;code&gt;choices&lt;/code&gt; (per OS)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--not-headless&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt; (= headless by default)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt; (&lt;code&gt;store_true&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_positive&lt;/code&gt; + &lt;code&gt;is_not_zero&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--logger&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;terminal+file&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;choices=LOGGERS_CHOICES&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--wait-timeout&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;≤ 60&lt;/code&gt;, &lt;code&gt;&amp;gt; 0&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--dont-force-delete-tmp-dirs&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt; (&lt;code&gt;store_true&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--only&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt; (&lt;code&gt;nargs=&amp;quot;+&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt; + mutex&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--exclude&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt; (&lt;code&gt;nargs=&amp;quot;+&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt; + mutex&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_WORKERS_AMOUNT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_LOGGER: SupportedLogger &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_BROWSER_AUTOMATION_TIMEOUT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_MAX_BROWSER_AUTOMATION_TIMEOUT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;literal&#34;&gt;&lt;code&gt;Literal[...]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#literal&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; SeleniumCliStoreKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;driver_path&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;profile_path&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;browser&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;headless&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;workers&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;logger&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wait_timeout&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;force_delete_tmp_dirs&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;only&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;exclude&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These keys are the store&amp;rsquo;s whole vocabulary. Call-sites use them: &lt;code&gt;store.get(&amp;quot;workers&amp;quot;)&lt;/code&gt;, &lt;code&gt;store.get(&amp;quot;browser&amp;quot;)&lt;/code&gt;, etc.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.04 — Loggers: PrintLogger, FileLogger, PrintAndFileLogger, MutedLogger</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/04-loggers/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/04-loggers/</guid>
      <description>&lt;h1 id=&#34;021104loggers-printlogger-filelogger-printandfilelogger-mutedlogger&#34;&gt;02.11.04 — Loggers: &lt;code&gt;PrintLogger&lt;/code&gt;, &lt;code&gt;FileLogger&lt;/code&gt;, &lt;code&gt;PrintAndFileLogger&lt;/code&gt;, &lt;code&gt;MutedLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021104loggers-printlogger-filelogger-printandfilelogger-mutedlogger&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/opinionated/loggers&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/loggers/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Four canonical &lt;code&gt;ILogger&lt;/code&gt; implementations + a &lt;code&gt;create_matching_logger&lt;/code&gt; factory. All opt-in.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;create_matching_logger&#34;&gt;&lt;code&gt;create_matching_logger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create_matching_logger&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LOGGERS_CHOICES &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;file&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;muted&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_matching_logger&lt;/span&gt;(mode: SupportedLogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ILogger:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; PrintLogger()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;file&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; FileLogger(base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;get_default_log_dir())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; PrintAndFileLogger(base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;get_default_log_dir())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;muted&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; MutedLogger()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unsupported logger mode: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;mode&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get_default_log_dir&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Path:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; Path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;cwd() &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.ocarina_logs&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ By default, logs land in &lt;code&gt;.ocarina_logs/&lt;/code&gt; at the user project root. Gitignored by convention.&lt;/p&gt;&#xA;&lt;h2 id=&#34;printlogger&#34;&gt;&lt;code&gt;PrintLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#printlogger&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;PrintLogger&lt;/span&gt;(ILogger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_prefix_thunk: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_prefix&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, prefix_thunk: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_prefix_thunk &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; prefix_thunk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_domain_taxonomy&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; taxonomy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;raw&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, stream&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; stream &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            stream &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, file&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;stream, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;info&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_print_log(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;[INFO]&amp;#34;&lt;/span&gt;, msg, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc, stream&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;sys&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;stdout)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# ... id. for critical / error / warning / debug / success / test_name ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;State&lt;/strong&gt;: &lt;code&gt;_prefix_thunk&lt;/code&gt; (Thunk) + &lt;code&gt;_domain_taxonomy&lt;/code&gt; (tuple).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Output&lt;/strong&gt;: &lt;code&gt;sys.stdout&lt;/code&gt; (info, debug, success, test_name) or &lt;code&gt;sys.stderr&lt;/code&gt; (critical, error, warning, exception).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Format&lt;/strong&gt;: typically &lt;code&gt;&amp;lt;prefix&amp;gt; [LEVEL] &amp;lt;message&amp;gt;&lt;/code&gt;, with ANSI colors if terminal.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;cleanup()&lt;/code&gt;&lt;/strong&gt;: no-op.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;filelogger&#34;&gt;&lt;code&gt;FileLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#filelogger&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;FileLogger&lt;/span&gt;(PrintLogger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        base_dir: Path,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        with_flush_effect: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        with_fallback_on_print_logger_when_no_taxonomy_effect: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; base_dir&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_with_flush_effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; with_flush_effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_with_fallback_on_print_logger_when_no_taxonomy_effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            with_fallback_on_print_logger_when_no_taxonomy_effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @property&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_log_file_path&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Path:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            file_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;joinpath(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            file_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        folder_path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;mkdir(parents&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, exist_ok&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; file_name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Taxonomy&lt;/th&gt;&#xA;          &lt;th&gt;Folder&lt;/th&gt;&#xA;          &lt;th&gt;File&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;          &lt;td&gt;→ Fallback to PrintLogger if enabled&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;annoncements&amp;quot;,)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;annoncements.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;e2e&amp;quot;, &amp;quot;Login&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir/e2e&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Login.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;e2e&amp;quot;, &amp;quot;Login&amp;quot;, &amp;quot;happy paths&amp;quot;, &amp;quot;Login without OTP&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir/e2e/Login/happy paths&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Login without OTP.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The &lt;strong&gt;taxonomy is the folder tree&lt;/strong&gt;. The DOCX reports (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/&#34;&gt;&lt;code&gt;05-plugins-reports.md&lt;/code&gt;&lt;/a&gt;) walk it recursively and emit one &lt;code&gt;.docx&lt;/code&gt; per test case.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.05 — Report plugins</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/</guid>
      <description>&lt;h1 id=&#34;021105report-plugins&#34;&gt;02.11.05 — Report plugins&lt;a class=&#34;anchor&#34; href=&#34;#021105report-plugins&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/opinionated/plugins/reports&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/plugins/reports/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;pretty_print_results&lt;/code&gt;, &lt;code&gt;results_to_json&lt;/code&gt;, &lt;code&gt;generate_docx_proof&lt;/code&gt;, &lt;code&gt;timing&lt;/code&gt;. All opt-in. Run sequentially or in parallel via &lt;code&gt;run_plugins&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pretty_print_results&#34;&gt;&lt;code&gt;pretty_print_results&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pretty_print_results&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/opinionated/plugins/reports/pretty_print_results.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;pretty_print_results&lt;/span&gt;(results: TestCycleResults, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, with_colors: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Display the full test results.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Campaign&#xA;• Suite&#xA;  &amp;gt; Test case 1&#xA;    » PASSED&#xA;  &amp;gt; Test case 2&#xA;    » FAILED&#xA;      → Error message&#xA;        ⫸ At step 3&#xA;  &amp;gt; Test case 3&#xA;    » SKIPPED&#xA;&#xA;Test results:&#xA;1 FAILED | 1 PASSED | 1 SKIPPED&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Aspect&lt;/th&gt;&#xA;          &lt;th&gt;Detail&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Hierarchy&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;3 levels: campaign &lt;code&gt;•&lt;/code&gt;, suite &lt;code&gt;&amp;gt;&lt;/code&gt;, case &lt;code&gt;»&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Levels&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;PASSED&lt;/code&gt; (green), &lt;code&gt;FAILED&lt;/code&gt; (red), &lt;code&gt;SKIPPED&lt;/code&gt; (gray)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Message + step number where it failed (&amp;quot;&lt;em&gt;At step 3&lt;/em&gt;&amp;quot;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Aggregated count at the bottom&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;results_to_json&#34;&gt;&lt;code&gt;results_to_json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#results_to_json&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;generate_json_results&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, results: TestCycleResults, output_dir: Path, logger: ILogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Write test results to a JSON file in results_dir.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    output_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;mkdir(parents&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, exist_ok&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max_stem_length &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max_attempts &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; _ &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(max_attempts):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        filename &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;uuid&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;uuid4()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;hex[:max_stem_length]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.json&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        file_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; output_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; filename&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; file_path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;exists():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Can&amp;#39;t generate unique JSON filename in: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;output_dir&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;Campaign&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;Suite&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;test_case&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fail&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        counter,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metadata&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_result_to_serializable&lt;/span&gt;(v: Any) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;dict&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; is_ok(v):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; is_fail(v):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fail&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;(v&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error)}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; TypeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Expected Ok or Fail instances, but got: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt;(v)&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Filename is an 8-char hex UUID → 16⁸ ≈ 4 billion options. No collisions in practice. 500 safety retries.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.06 — bootstrap &#43; run_plugins</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/06-bootstrap-launcher/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/06-bootstrap-launcher/</guid>
      <description>&lt;h1 id=&#34;021106bootstrap--run_plugins&#34;&gt;02.11.06 — &lt;code&gt;bootstrap&lt;/code&gt; + &lt;code&gt;run_plugins&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021106bootstrap--run_plugins&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/launcher/bootstrap.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/launcher/bootstrap.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Entry point of an Ocarina project.&lt;br&gt;&#xA;Three steps: &lt;code&gt;cycle.run_all → run_plugins → post_exec&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;bootstrap&lt;/span&gt;[T](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_cycle: TestCycle[T],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_plugins: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post_exec: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_cycle&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run_all(saturate_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;saturate_workers)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_plugins(results)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; post_exec:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        post_exec(results)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;order&#34;&gt;Order&lt;a class=&#34;anchor&#34; href=&#34;#order&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. cycle.run_all()  →  results&#xA;2. run_plugins(results)&#xA;3. post_exec(results) (optional)&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Step&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Why this order&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;All tests run&lt;/td&gt;&#xA;          &lt;td&gt;We need &lt;code&gt;results&lt;/code&gt; for the rest&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;Plugins fire (DOCX, JSON, etc.)&lt;/td&gt;&#xA;          &lt;td&gt;Must run &lt;em&gt;before&lt;/em&gt; &lt;code&gt;post_exec&lt;/code&gt; since it can take generation time&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;Post-exec (pretty_print + sys.exit)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Last&lt;/em&gt; thing — after this, the process is dead&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;typing&#34;&gt;Typing&lt;a class=&#34;anchor&#34; href=&#34;#typing&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_plugins: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;post_exec: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Both receive the &lt;strong&gt;&lt;code&gt;results&lt;/code&gt;&lt;/strong&gt; as an argument:&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
