<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaëtan Montury</title>
    <description>The latest articles on DEV Community by Gaëtan Montury (@pytgaen).</description>
    <link>https://dev.to/pytgaen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3959432%2Ffa4ad879-c502-4093-a885-de165020224b.png</url>
      <title>DEV Community: Gaëtan Montury</title>
      <link>https://dev.to/pytgaen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pytgaen"/>
    <language>en</language>
    <item>
      <title>8 small data transforms I don't want to write as shell glue anymore</title>
      <dc:creator>Gaëtan Montury</dc:creator>
      <pubDate>Thu, 02 Jul 2026 14:28:00 +0000</pubDate>
      <link>https://dev.to/pytgaen/8-small-data-transforms-i-dont-want-to-write-as-shell-glue-anymore-832</link>
      <guid>https://dev.to/pytgaen/8-small-data-transforms-i-dont-want-to-write-as-shell-glue-anymore-832</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🦀🐍 Practical fimod examples for small CI/config data transforms with a Python-like taste.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/pytgaen/replacing-fragile-python3-c-scripts-in-ci-with-fimod-1kbf"&gt;first article&lt;/a&gt;,&lt;br&gt;
I wrote about why I built &lt;strong&gt;fimod&lt;/strong&gt;: I kept seeing tiny &lt;code&gt;python3 -c&lt;/code&gt; scripts in CI pipelines, doing just enough JSON/YAML/CSV work to be useful, and just enough shell quoting to become annoying.&lt;/p&gt;

&lt;p&gt;This article is more concrete.&lt;/p&gt;

&lt;p&gt;Here are &lt;strong&gt;8 small transforms&lt;/strong&gt; that I do not want to keep rewriting as shell glue, ad-hoc Python snippets, or copied scripts between repositories.&lt;/p&gt;

&lt;p&gt;fimod is not trying to replace jq or yq. Those are excellent tools. I see fimod more as a small &lt;strong&gt;data-shaping Swiss army knife&lt;/strong&gt;: a few megabytes, Python-like expressions, common structured formats, reusable molds, and a controlled execution surface.&lt;/p&gt;

&lt;p&gt;📚 Website/docs: &lt;a href="https://pytgaen.github.io/fimod/" rel="noopener noreferrer"&gt;https://pytgaen.github.io/fimod/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
⭐ Repository: &lt;a href="https://github.com/pytgaen/fimod" rel="noopener noreferrer"&gt;https://github.com/pytgaen/fimod&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Extract one value for a shell script
&lt;/h2&gt;

&lt;p&gt;Sometimes a CI job only needs one value from a structured file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1.2.3"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With fimod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; package.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'data["version"]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output-format&lt;/span&gt; txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes it easy to use in shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; package.json &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'data["version"]'&lt;/span&gt; &lt;span class="nt"&gt;--output-format&lt;/span&gt; txt&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No JSON boilerplate, no quotes around the string, no temporary script.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Validate a config file in CI
&lt;/h2&gt;

&lt;p&gt;For boolean checks, &lt;code&gt;--check&lt;/code&gt; suppresses stdout and maps truthiness to the exit code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; deploy.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'all(k in data for k in ["image", "replicas", "port"])'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For clearer error messages, use &lt;code&gt;gk_assert&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; deploy.yaml &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'
def transform(data, **_):
    gk_assert("image" in data, "missing image")
    gk_assert("replicas" in data, "missing replicas")
    gk_assert("port" in data, "missing port")
    return True
'&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On failure, the error messages go to stderr and the process exits non-zero — the shape CI expects.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Read an HTTP API directly
&lt;/h2&gt;

&lt;p&gt;For small API transforms, fimod can read HTTPS URLs directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; https://api.github.com/repos/pytgaen/fimod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'{"name": data["name"], "stars": data["stargazers_count"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fimod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not meant to replace a full HTTP client in an application. But for CI metadata, release scripts, or quick API-to-config glue, it avoids another &lt;code&gt;curl | jq | sed&lt;/code&gt; chain.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Extract named regex captures
&lt;/h2&gt;

&lt;p&gt;fimod injects regex helpers into every transform. &lt;code&gt;re_search&lt;/code&gt; returns structured data, including named captures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tag":"release-v2.4.1"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | fimod s &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'re_search(r"(?P&amp;lt;major&amp;gt;[0-9]+)\.(?P&amp;lt;minor&amp;gt;[0-9]+)", data["tag"])["named"]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"major"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, the regex helpers use Rust's &lt;code&gt;fancy-regex&lt;/code&gt; crate, with PCRE-like features such as lookahead, lookbehind, backreferences, and named captures.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Flatten nested fields to CSV
&lt;/h2&gt;

&lt;p&gt;APIs often return nested JSON, while the next step wants a flat CSV artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Paris"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:{}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; users.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'[{"name": u["name"], "email": u["email"], "city": dp_get(u, "address.city", "unknown")} for u in data]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; contacts.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,email,city
Alice,alice@example.com,Paris
Bob,bob@example.com,unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dp_get&lt;/code&gt; avoids a small pile of defensive nested &lt;code&gt;dict.get(...)&lt;/code&gt; calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Hash sensitive fields before exporting
&lt;/h2&gt;

&lt;p&gt;For anonymized fixtures or safer artifacts, hashing is built in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; people.csv &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'[{**row, "email": hs_sha256(row["email"])} for row in data]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; people-anon.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,email
Alice,alice@example.com
Bob,bob@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,email
Alice,ff8d9819fc0e12bf0d24892e45987e249a28dce836a85cad60e28eaaa8c6d976
Bob,5ff860bf1190596c7188ab851db691f0f3169c453936e9e1eba2f9a47f7a0018
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is not that hashing is hard in Python. It is not. The point is that in a small CI transform, I do not want to import and wire another script just for that.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Generate text/config with MiniJinja
&lt;/h2&gt;

&lt;p&gt;fimod is not limited to data-to-data transforms. It can also render text from structured data using MiniJinja-powered helpers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"app":"api","host":"localhost","port":8080}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | fimod s &lt;span class="nt"&gt;--output-format&lt;/span&gt; txt &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'tpl_render_str("APP={{ app }}\nURL=http://{{ host }}:{{ port }}\n", data)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP=api
URL=http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens the door to &lt;code&gt;.env&lt;/code&gt; files, Markdown snippets, Dockerfiles, small config files, or release notes generated from structured data.&lt;/p&gt;

&lt;p&gt;For larger templates, fimod also supports rendering template files from directory molds with &lt;code&gt;tpl_render_from_mold(...)&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Reuse a mold from a registry
&lt;/h2&gt;

&lt;p&gt;One-liners are nice, but the more interesting part is sharing transforms.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;mold&lt;/strong&gt; is a reusable transform script. A &lt;strong&gt;registry&lt;/strong&gt; lets you call molds by name with &lt;code&gt;@name&lt;/code&gt; instead of copying scripts between repositories.&lt;/p&gt;

&lt;p&gt;For example, with the example registry configured, you can call the shared &lt;code&gt;pick_fields&lt;/code&gt; mold by name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; contacts.csv &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; @pick_fields &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nv"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;name,email &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; contacts-public.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because fimod resolves &lt;code&gt;@pick_fields&lt;/code&gt; from a configured registry. In a fresh environment, you can configure the default registry once, or point this command at the official example registry explicitly with &lt;code&gt;FIMOD_REGISTRY=https://github.com/pytgaen/fimod/tree/main/molds&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,email,role
Alice,alice@example.com,admin
Bob,bob@example.com,user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a team, that registry can be your own Git repository. That is where fimod becomes more than a one-liner tool: reviewed transforms can be reused across projects without copy-pasting another tiny Python script everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  So when would I use this?
&lt;/h2&gt;

&lt;p&gt;Fimod is a great fit for CI/config data plumbing: one small but powerful binary that can read many structured formats, pull input from HTTP when the data lives behind an API, and reuse molds when a transform should become shared project knowledge.&lt;/p&gt;

&lt;p&gt;The registry and sandbox matter for that last part. They make sharing a transform less about copying a script around, and more about resolving known code from a known place, with a controlled execution surface.&lt;/p&gt;

&lt;p&gt;I would reach for it when I need a small, explicit data transform in a pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract one value;&lt;/li&gt;
&lt;li&gt;validate a config;&lt;/li&gt;
&lt;li&gt;reshape JSON/YAML/CSV/TOML;&lt;/li&gt;
&lt;li&gt;read a small API response;&lt;/li&gt;
&lt;li&gt;hash or mask fields;&lt;/li&gt;
&lt;li&gt;generate a tiny text artifact;&lt;/li&gt;
&lt;li&gt;reuse a shared transform as a mold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where I want fimod to shine: &lt;strong&gt;boring CI/config transforms with less glue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you try one of these examples, I would start with a small file you already manipulate in a script today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ If the idea looks useful, a GitHub star helps the project be discovered.&lt;/li&gt;
&lt;li&gt;💬 If you have a quick reaction, a comment here on dev.to is perfect.&lt;/li&gt;
&lt;li&gt;🐛 If an example breaks or the docs are unclear, a short GitHub issue with the command you tried is more than enough.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>cli</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Replacing fragile python3 -c scripts in CI with fimod</title>
      <dc:creator>Gaëtan Montury</dc:creator>
      <pubDate>Tue, 02 Jun 2026 14:26:30 +0000</pubDate>
      <link>https://dev.to/pytgaen/replacing-fragile-python3-c-scripts-in-ci-with-fimod-1kbf</link>
      <guid>https://dev.to/pytgaen/replacing-fragile-python3-c-scripts-in-ci-with-fimod-1kbf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🦀🐍 A small Rust CLI for Python-like data transforms without Python installed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I keep running into the same kind of script in CI pipelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import json,sys; data=json.load(sys.stdin); print(json.dumps(...))'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It usually starts as a quick one-liner. Then it grows a condition. Then it needs to read YAML instead of JSON. Then someone has to escape quotes inside quotes inside YAML. Eventually it becomes either a small script committed somewhere, or another piece of shell glue that nobody really wants to maintain.&lt;/p&gt;

&lt;p&gt;That is the problem I built &lt;strong&gt;fimod&lt;/strong&gt; for. 🛠️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;fimod&lt;/strong&gt; is a small Rust CLI that transforms structured data - JSON, YAML, TOML, CSV, NDJSON, text lines - using Python-like expressions powered by &lt;a href="https://github.com/pydantic/monty" rel="noopener noreferrer"&gt;Pydantic Monty&lt;/a&gt;, a Python runtime written in Rust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No system Python is required. No &lt;code&gt;pip install&lt;/code&gt;.&lt;/strong&gt; No custom mini-language to learn if you already know basic Python. The standard binary is only a few megabytes - about &lt;strong&gt;3.2 MB&lt;/strong&gt; on my current Linux install - and still includes parsing, serialization, HTTP input, and a set of Rust-powered data helpers.&lt;/p&gt;

&lt;p&gt;The goal is not to replace Python. The goal is to remove the boilerplate around small data transformations in CI, scripts, and config workflows.&lt;/p&gt;

&lt;p&gt;📚 Website/docs: &lt;a href="https://pytgaen.github.io/fimod/" rel="noopener noreferrer"&gt;https://pytgaen.github.io/fimod/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ Repository: &lt;a href="https://github.com/pytgaen/fimod" rel="noopener noreferrer"&gt;https://github.com/pytgaen/fimod&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐍 write small transforms with Python-like expressions;&lt;/li&gt;
&lt;li&gt;🦀 ship them as a small Rust binary;&lt;/li&gt;
&lt;li&gt;📦 read/write JSON, YAML, TOML, CSV, NDJSON and text;&lt;/li&gt;
&lt;li&gt;🔁 keep CI data plumbing explicit and shell-friendly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 A tiny example
&lt;/h2&gt;

&lt;p&gt;Suppose a CI job has a JSON file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"worker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a regular Python one-liner, the useful logic is often hidden inside JSON loading, stdin handling, output formatting, and shell escaping.&lt;/p&gt;

&lt;p&gt;With fimod, the parsed input is already available as &lt;code&gt;data&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; services.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'[svc for svc in data if svc["active"]]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; active-services.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The expression is just the transformation.&lt;/strong&gt; fimod handles reading, parsing, serializing, and writing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Validate config in CI
&lt;/h2&gt;

&lt;p&gt;A common CI task is checking that a config file contains the fields a deployment&lt;br&gt;
expects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; deploy.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'all(k in data for k in ["image", "replicas", "port"])'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--check&lt;/code&gt; suppresses stdout and uses the truthiness of the result as the process&lt;br&gt;
exit code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exit &lt;code&gt;0&lt;/code&gt; if the result is truthy;&lt;/li&gt;
&lt;li&gt;exit &lt;code&gt;1&lt;/code&gt; if the result is falsy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes it easy to use in shell scripts and CI jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; deploy.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'all(k in data for k in ["image", "replicas", "port"])'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--check&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Invalid deploy config"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more explicit error messages, fimod also provides validation helpers such as&lt;br&gt;
&lt;code&gt;gk_assert&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; deploy.yaml &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'
def transform(data, **_):
    gk_assert("image" in data, "missing image")
    gk_assert("replicas" in data, "missing replicas")
    gk_assert("port" in data, "missing port")
    return True
'&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;gk_assert&lt;/code&gt; reports each missing field on stderr and sets a non-zero exit code on failure. The final &lt;code&gt;return True&lt;/code&gt; makes the successful case truthy for &lt;code&gt;--check&lt;/code&gt;, while &lt;code&gt;--check&lt;/code&gt; keeps stdout silent.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔀 Convert formats without changing the logic
&lt;/h2&gt;

&lt;p&gt;Because parsing and serialization are handled by fimod, the same expression can work across different input formats.&lt;/p&gt;

&lt;p&gt;YAML to TOML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; config.yaml &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; config.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSV to JSON, with an explicit cast for &lt;code&gt;age&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; users.csv &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'[{**row, "age": int(row["age"])} for row in data]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; users.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSV values arrive in fimod as strings, so &lt;code&gt;int(row["age"])&lt;/code&gt; is intentional.&lt;br&gt;
Modern &lt;code&gt;yq&lt;/code&gt; can do CSV-to-JSON very well too - for example&lt;br&gt;
&lt;code&gt;yq -p=csv -o=json '.' users.csv&lt;/code&gt; - so this is not meant as a "yq cannot do this" example. The point is that when the transformation grows, the logic stays in familiar Python-like syntax.&lt;/p&gt;

&lt;p&gt;Extract one value for a shell variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; package.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'data["version"]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output-format&lt;/span&gt; txt&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The point is not that these examples are impossible with other tools.&lt;/em&gt; They are not. The point is that the transformation stays focused on the data, while fimod handles the repetitive I/O details.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧰 What about jq and yq?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;jq and yq are excellent tools.&lt;/strong&gt; If your team already knows them and they solve the problem cleanly, keep using them.&lt;/p&gt;

&lt;p&gt;fimod is useful when you prefer Python-like expressions and want the same command shape across JSON, YAML, TOML, CSV, NDJSON, and text. It is not meant as a "yq cannot do this" argument; it is a different tradeoff.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Reusable transforms: molds
&lt;/h2&gt;

&lt;p&gt;One-liners are convenient, but CI pipelines eventually grow shared logic.&lt;/p&gt;

&lt;p&gt;In fimod, a reusable transform is called a &lt;strong&gt;mold&lt;/strong&gt;. It is a Python-like script with a &lt;code&gt;transform(data, **_)&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# cleanup.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; contacts.csv &lt;span class="nt"&gt;-m&lt;/span&gt; cleanup.py &lt;span class="nt"&gt;-o&lt;/span&gt; contacts.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Molds can also receive parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filter_by_field.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; users.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; filter_by_field.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nv"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The really practical part is that molds are not just local scripts.&lt;br&gt;
&lt;strong&gt;They can be shared through registries&lt;/strong&gt; and referenced by name with &lt;code&gt;@&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, a CI job can point fimod at a local shared mold directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FIMOD_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./molds &lt;span class="se"&gt;\&lt;/span&gt;
  fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; contacts.csv &lt;span class="nt"&gt;-m&lt;/span&gt; @cleanup &lt;span class="nt"&gt;-o&lt;/span&gt; contacts.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or at the official fimod example registry on GitHub, and call a real published mold by name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FIMOD_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/pytgaen/fimod/tree/main/molds &lt;span class="se"&gt;\&lt;/span&gt;
  fimod s &lt;span class="nt"&gt;-i&lt;/span&gt; contacts.csv &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-m&lt;/span&gt; @pick_fields &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nv"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;name,email &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; contacts-public.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real team setup, that registry can be your own Git repository. The important bit is that every project can call the same reviewed transform by name instead of copying another tiny script into every repo.&lt;/p&gt;

&lt;p&gt;That is where molds become more than a convenience: they become &lt;strong&gt;shared data recipes for CI&lt;/strong&gt;. I will cover registries more deeply in a separate article, but even the basic &lt;code&gt;@name&lt;/code&gt; pattern is already useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 Why not just use Python?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For many tasks, you should use Python.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But plain Python is not a unified structured-data CLI out of the box. JSON and CSV are in the standard library, but YAML usually means an extra dependency, TOML support depends on the Python version, and NDJSON, format detection, stdin/stdout plumbing, shell-friendly output, and cross-format conversion all add glue code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That glue is fine in a real application.&lt;/em&gt; In a CI job, it often turns a small transformation into dependency management, quoting, and boilerplate.&lt;/p&gt;

&lt;p&gt;fimod is useful when the script is small enough that a full Python file feels heavy, but important enough that a quoted &lt;code&gt;python3 -c&lt;/code&gt; command is painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ What I am not covering here
&lt;/h2&gt;

&lt;p&gt;fimod also has HTTP input, MiniJinja-powered templating for data-to-text outputs, and built-in helpers for regex, nested fields, grouping, hashing, validation, diagnostics, and environment substitution. The regex helpers are backed by Rust's &lt;code&gt;fancy-regex&lt;/code&gt; crate, with PCRE-like features such as lookahead, lookbehind, backreferences, and named captures.&lt;/p&gt;

&lt;p&gt;That means later articles can cover things like fetching an API response, extracting fields, hashing sensitive values, or rendering &lt;code&gt;.env&lt;/code&gt;, Markdown, Dockerfile, and config files from structured data.&lt;/p&gt;

&lt;p&gt;Those deserve separate articles. This one is intentionally focused on the first use case: replacing fragile CI one-liners and sharing transforms as molds.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Important limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;fimod is early-stage software.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few things are intentionally not hidden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fimod runs on Monty, not CPython;&lt;/li&gt;
&lt;li&gt;it does not support arbitrary PyPI packages;&lt;/li&gt;
&lt;li&gt;molds cannot freely access the filesystem or the network;&lt;/li&gt;
&lt;li&gt;the project is maintained part-time;&lt;/li&gt;
&lt;li&gt;the API and behavior may still evolve as Monty and fimod mature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those constraints are part of the design. fimod is not trying to be a general Python runtime. It is trying to be a practical data-shaping CLI with explicit inputs and controlled execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Try it
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/pytgaen/fimod/main/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a first transform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[{"name":"Alice"},{"name":"Bob"}]'&lt;/span&gt; | fimod s &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'len(data)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📚 Website/docs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pytgaen.github.io/fimod/" rel="noopener noreferrer"&gt;https://pytgaen.github.io/fimod/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pytgaen/fimod" rel="noopener noreferrer"&gt;https://github.com/pytgaen/fimod&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try fimod, I would start with one small, boring transform in a CI job or config workflow. 🧪&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ If the idea looks useful, a GitHub star helps the project be discovered.&lt;/li&gt;
&lt;li&gt;💬 If you have a quick reaction, a comment here on dev.to is perfect.&lt;/li&gt;
&lt;li&gt;🐛 If something feels unclear - the install, the command shape, the docs, the error message, or a small improvement idea - a short GitHub issue with the you tried is more than enough.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>cicd</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
