<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: yaythomas</title>
    <description>The latest articles on Forem by yaythomas (@yaythomas).</description>
    <link>https://forem.com/yaythomas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F477542%2F4df05e8d-4afd-4bda-aa55-051d56ab19ba.jpeg</url>
      <title>Forem: yaythomas</title>
      <link>https://forem.com/yaythomas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yaythomas"/>
    <language>en</language>
    <item>
      <title>durable functions... AWS Lambda beyond 15mins, with automatic durability and state management!</title>
      <dc:creator>yaythomas</dc:creator>
      <pubDate>Mon, 08 Dec 2025 02:29:02 +0000</pubDate>
      <link>https://forem.com/yaythomas/durable-functions-aws-lambda-beyond-15mins-with-automatic-durability-and-state-management-2579</link>
      <guid>https://forem.com/yaythomas/durable-functions-aws-lambda-beyond-15mins-with-automatic-durability-and-state-management-2579</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aws/aws-lambda-durable-functions-build-workflows-that-last-3ac7" class="crayons-story__hidden-navigation-link"&gt;AWS Lambda Durable Functions: Build Workflows That Last&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aws"&gt;
            &lt;img alt="AWS logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1726%2F2a73f1e6-7995-4348-ae37-44b064274c59.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/edjgeek" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2745619%2F51d975f9-b5eb-4ca5-897d-239f41bffb39.jpg" alt="edjgeek profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/edjgeek" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Eric D Johnson
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Eric D Johnson
                
              
              &lt;div id="story-author-preview-content-3079760" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/edjgeek" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2745619%2F51d975f9-b5eb-4ca5-897d-239f41bffb39.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Eric D Johnson&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aws" class="crayons-story__secondary fw-medium"&gt;AWS&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aws/aws-lambda-durable-functions-build-workflows-that-last-3ac7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 3 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aws/aws-lambda-durable-functions-build-workflows-that-last-3ac7" id="article-link-3079760"&gt;
          AWS Lambda Durable Functions: Build Workflows That Last
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/serverless"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;serverless&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aws"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aws&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/lambda"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;lambda&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/durable"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;durable&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aws/aws-lambda-durable-functions-build-workflows-that-last-3ac7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;26&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aws/aws-lambda-durable-functions-build-workflows-that-last-3ac7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

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

&lt;/div&gt;




</description>
      <category>serverless</category>
      <category>aws</category>
      <category>lambda</category>
      <category>durable</category>
    </item>
    <item>
      <title>Comparison of Python TOML parser libraries</title>
      <dc:creator>yaythomas</dc:creator>
      <pubDate>Tue, 14 Dec 2021 16:56:08 +0000</pubDate>
      <link>https://forem.com/pypyr/comparison-of-python-toml-parser-libraries-595e</link>
      <guid>https://forem.com/pypyr/comparison-of-python-toml-parser-libraries-595e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Update February 2022: Since the original article was written below, &lt;a href="https://www.python.org/dev/peps/pep-0680/"&gt;PEP 680 to add TOML Parsing in the Standard Library&lt;/a&gt; was approved.&lt;br&gt;
So &lt;code&gt;tomli&lt;/code&gt;, as recommended below, will actually be included in 3.11.&lt;/p&gt;

&lt;p&gt;If, like most of us, you're going to have to support older versions of Python for a few years yet, read on. . . this article talks you through your options!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://pypyr.io"&gt;pypyr automation pipeline task-runner&lt;/a&gt; open-source project recently added &lt;a href="https://pypyr.io/updates/releases/v5.1.0/"&gt;TOML parsing &amp;amp; writing&lt;/a&gt; functionality as a core feature. To this end, I researched the available free &amp;amp; open-source Python TOML parser libraries to figure out which option to use.&lt;/p&gt;

&lt;p&gt;If you're interested in a walkthrough of the decision making process, this is documented in an &lt;a href="https://adr.github.io"&gt;Architecture Decision Record (ADR)&lt;/a&gt; you can read here: &lt;a href="https://github.com/pypyr/pypyr/blob/main/docs/adr/0003-toml-in-core.md"&gt;adr003 toml in the pypyr core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully sharing my notes helps someone else going through this process to save some time... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://toml.io/en/"&gt;TOML&lt;/a&gt; is becoming a popular configuration format, and unavoidably so in Python in the shape of &lt;code&gt;pyproject.toml&lt;/code&gt; as accepted by &lt;a href="https://www.python.org/dev/peps/pep-0518/"&gt;PEP518&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since "best" is a loose term best left to click-bait headlines (hoho, see what I did there? 😉), instead of asking "which TOML parser is the best?" the more sensible question to answer is which TOML library more suits the requirements of your project with the least negatives?&lt;/p&gt;

&lt;h2&gt;
  
  
  Python TOML libraries at a glance
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/hukkin/tomli"&gt;tomli&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Relatively fast read-only parsing.&lt;/li&gt;
&lt;li&gt;Companion library &lt;code&gt;tomli-w&lt;/code&gt; for writing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sdispater/tomlkit"&gt;tomlkit&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;Round-trip white-space/style preserving.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/uiri/toml"&gt;toml&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This was initially vendored in pip itself to deal with &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Even so &lt;code&gt;pip&lt;/code&gt; has since moved to &lt;code&gt;tomli&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/avakar/pytoml"&gt;pytoml&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Abandoned by the creator for eminently sensible reasons (interesting read too... &lt;a href="https://github.com/avakar/pytoml/issues/15"&gt;https://github.com/avakar/pytoml/issues/15&lt;/a&gt;) But let's not get into an argument over whether a shiny new fashion in config formats is in fact doing anything better than the previous fashions in config management...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/alethiophile/qtoml"&gt;qtoml&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Still on TOML v0.5.0.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that these are the pure Python parsers - there are also others that are basically interop wrappers for fast C++ or Rust libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/bobfang1992/pytomlpp"&gt;pytomlpp&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Python wrapper for the &lt;a href="https://marzer.github.io/tomlplusplus/"&gt;toml++&lt;/a&gt; C++ library.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/samuelcolvin/rtoml"&gt;rtoml&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Python wrapper for fast parsing written in Rust.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If performance is your main concern, then the C++/Rust implementations might serve your needs, assuming you're fine with these not being pure Python packages.&lt;/p&gt;

&lt;p&gt;Let's investigate the pure Python libraries in greater depth:&lt;/p&gt;

&lt;h3&gt;
  
  
  tomlkit
&lt;/h3&gt;

&lt;p&gt;Of the available options, &lt;em&gt;only&lt;/em&gt; &lt;code&gt;tomlkit&lt;/code&gt; supports style-preserving roundtrip parsing. Furthermore, &lt;code&gt;tomlkit&lt;/code&gt; was created for the express purpose of handling TOML parsing for the &lt;a href="https://python-poetry.org"&gt;poetry&lt;/a&gt; tool. As this is one of the 2 most popular new PEP517 &amp;amp; PEP518 Python build systems there is some comfort to be had in the wide adoption of a very actively used tool that means a greater likelihood of continued maintenance &amp;amp; support, and specifically that &lt;code&gt;pyproject.toml&lt;/code&gt; files &lt;em&gt;should&lt;/em&gt; parse without surprises.&lt;/p&gt;

&lt;p&gt;TOMLKit only lists itself as &lt;code&gt;1.0.0rc1&lt;/code&gt; compliant. Looking at the &lt;a href="https://github.com/toml-lang/toml/releases/"&gt;TOML spec release history&lt;/a&gt; delta of &lt;code&gt;rc1&lt;/code&gt; vs &lt;code&gt;v1&lt;/code&gt;, it only looks like clarifications &amp;amp; administrative/documentation updates  -there doesn't &lt;em&gt;seem&lt;/em&gt; to be anything notable missing or functionally different in &lt;code&gt;rc1&lt;/code&gt; as opposed to &lt;code&gt;v1&lt;/code&gt;. It's not impossible that I missed something, though - but given &lt;code&gt;TOMLKit&lt;/code&gt;'s wide usage via &lt;code&gt;poetry&lt;/code&gt;, I would expect obvious out-of-date spec handling to have been noticed by someone somewhere, and I see none such in the &lt;a href="https://github.com/sdispater/tomlkit/issues"&gt;issues list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is a but... &lt;code&gt;TOMLKit&lt;/code&gt; outputs custom types rather than just the standard Python built-ins like &lt;code&gt;dict&lt;/code&gt;. Specifically it represents tables with classes like &lt;code&gt;class Table(Item,MutableMapping, dict)&lt;/code&gt; or &lt;code&gt;class InlineTable(Item, MutableMapping, dict)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;(See here for &lt;a href="https://github.com/sdispater/tomlkit/blob/master/tomlkit/items.py"&gt;TOMLkit API types&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;The constructors for these do NOT allow any of these to instantiate like a standard &lt;code&gt;Mapping&lt;/code&gt; type does - which may or may not fit your needs, depending on what exactly you're doing. It probably doesn't really matter for most purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  toml
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;toml&lt;/code&gt; library is a largely historical artifact at this point. Not only is it well behind on implementing TOML v1, but also because of a lack of maintenance on extant functionality. &lt;/p&gt;

&lt;p&gt;Even &lt;code&gt;pip&lt;/code&gt; itself &lt;a href="https://github.com/pypa/pip/pull/10035"&gt;has moved from vendoring toml to tomli&lt;/a&gt;. This exodus from &lt;code&gt;toml&lt;/code&gt; to &lt;code&gt;tomli&lt;/code&gt; includes the very prominent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pypa/pip/issues/10034"&gt;pip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python/typeshed/issues/6022"&gt;typeshed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/psf/black/issues/2280"&gt;black&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python/mypy/pull/10824"&gt;mypy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nedbat/coveragepy/issues/1180"&gt;coverage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pypa/flit/pull/438"&gt;flit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(The links are to the issues/PRs discussing the reasons why...)&lt;/p&gt;

&lt;h3&gt;
  
  
  tomli
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tomli&lt;/code&gt;, then, seems to be where the Python community in general is coalescing for a "standard" TOML parser. &lt;code&gt;tomli&lt;/code&gt; is read-only. For write functionality there is the companion library &lt;a href="https://github.com/hukkin/tomli-w"&gt;tomli-w&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tomli&lt;/code&gt; is explicitly TOML v1.0 compliant.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tomli&lt;/code&gt; is &lt;em&gt;significantly&lt;/em&gt; faster than TOMLKit. It does not, however, preserve style/whitespace like &lt;code&gt;tomlkit&lt;/code&gt; does. For most use-cases, arguably TOML reading is the important part...&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;tomlkit&lt;/code&gt; if you need to round-trip &amp;amp; preserve style + comments.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;tomli&lt;/code&gt; if you're just after reading a config file or write some output without caring about the formatting too much.&lt;/p&gt;

&lt;p&gt;Use the Rust/C++ interop libraries if performance is your main concern and you do not have limitations on using packages that aren't pure Python.&lt;/p&gt;

&lt;p&gt;In the case of pypyr, &lt;code&gt;tomli&lt;/code&gt; matched the requirements with the least trade-offs. And as a mini-review, it was a joy to use 😄.&lt;/p&gt;

&lt;p&gt;If you're interested in seeing a real-world usage example for &lt;code&gt;tomli&lt;/code&gt; &amp;amp; &lt;code&gt;tomli-w&lt;/code&gt;, you can check it out in action here: &lt;a href="https://github.com/pypyr/pypyr/blob/main/pypyr/toml.py"&gt;https://github.com/pypyr/pypyr/blob/main/pypyr/toml.py&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Simplify your automation scripts with a low-code task-runner</title>
      <dc:creator>yaythomas</dc:creator>
      <pubDate>Mon, 05 Oct 2020 13:20:12 +0000</pubDate>
      <link>https://forem.com/pypyr/simplify-your-automation-scripts-with-a-low-code-task-runner-2981</link>
      <guid>https://forem.com/pypyr/simplify-your-automation-scripts-with-a-low-code-task-runner-2981</guid>
      <description>&lt;p&gt;&lt;a href="https://pypyr.io" rel="noopener noreferrer"&gt;pypyr automation task-runner&lt;/a&gt; is a free open-source tool that lets you script sequential task workflow steps in a pipeline using yaml. You get conditional execution, loops, error handling &amp;amp; retries without writing custom code.&lt;/p&gt;

&lt;p&gt;pypyr runs &lt;a href="https://pypyr.io/docs/cli/run-a-pipeline/" rel="noopener noreferrer"&gt;pipelines from a clear straightforward CLI&lt;/a&gt;. You can also use this as a library programatically from your own code - the &lt;a href="https://pypyr.io/docs/api/run-pipeline/" rel="noopener noreferrer"&gt;pypyr API to run a pipeline&lt;/a&gt; is undemanding and clearly documented!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you use a task-runner?
&lt;/h2&gt;

&lt;p&gt;You know how your automation scripts usually start off with a couple of innocent lines of bash, or maybe python? &lt;/p&gt;

&lt;p&gt;And then they get out of hand?&lt;/p&gt;

&lt;p&gt;And then before you know it you've got a big clumsy monster that you really hope doesn't break because you're too scared to touch it? &lt;/p&gt;

&lt;p&gt;And then you have to code &amp;amp; test the repetitive not-all-that-interesting plumbing of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sequencing tasks&lt;/li&gt;
&lt;li&gt;parse &amp;amp; validate cli input arguments&lt;/li&gt;
&lt;li&gt;parse config files, write output files&lt;/li&gt;
&lt;li&gt;handling errors&lt;/li&gt;
&lt;li&gt;retrying commands on error&lt;/li&gt;
&lt;li&gt;running a group of commands in a loop&lt;/li&gt;
&lt;li&gt;invoking the cli from python or invoking python from the cli&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Streamline automation with a task-runner
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypyr.io" rel="noopener noreferrer"&gt;pypyr task-runner&lt;/a&gt; helps with all that. With pypyr you create your automation workflows in a human-friendly yaml pipeline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add loops, error-handling &amp;amp; automatic retries to your command &amp;amp; program sequences, without having to write &amp;amp; troubleshoot code to do so.&lt;/li&gt;
&lt;li&gt;Create your own cli app, with your own custom command line arguments, without having to do all that arg parsing &amp;amp; validation yourself. &lt;/li&gt;
&lt;li&gt;Control-of-flow where you conditionally execute or skip steps based upon input expressions &amp;amp; switches.&lt;/li&gt;
&lt;li&gt;Easily work with config files like json, yaml or plain old text to parse &amp;amp; substitute values. &lt;/li&gt;
&lt;li&gt;No-code to low-code way of sequencing commands &amp;amp; programs, while still having the power to drop into full-on Python if you need to express something more complicated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add automation to existing utilities without writing code
&lt;/h2&gt;

&lt;p&gt;Here is an example pipeline to retry a &lt;code&gt;curl&lt;/code&gt; command up to four times until it's successful, at 0.5 second intervals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4kzkd76v3p9rppgn43ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4kzkd76v3p9rppgn43ks.png" alt="automatically retry any command without writing code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason I first wrote pypyr, is because when I'm writing automation scripts, I find myself jumping between bash/shell and a more advanced language like Python to get things done efficiently. &lt;/p&gt;

&lt;p&gt;For example. . . sometimes a quick &lt;code&gt;sed&lt;/code&gt; from the cli is quicker to implement, sometimes more advanced string parsing in Python is necessary - sometimes both things are necessary at different parts in the same automation sequence.&lt;/p&gt;

&lt;p&gt;Some tools you need to invoke to automate your tasks only have CLIs, other tools only have APIs. . .&lt;/p&gt;

&lt;p&gt;The famous Unix philosophy of building programs that do one thing and do them well has given us a lot of battle-tested amazing utilities that have been around for decades (think &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;. . .)&lt;/p&gt;

&lt;p&gt;Why not use these, rather than reinvent the wheel? So the point of pypyr is to make it easier for you to stitch together those awesome utilities, do some of your own scripting in between them, and generally automate it all without the usual repetitive pain points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a task-runner if you could just code it all yourself instead?
&lt;/h2&gt;

&lt;p&gt;The obvious hard-core developer response is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;yeah, I just code that myself, why bother with this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yes, of course, you totally can code all of the surrounding, boring, repetitive, not-all-that-interesting bits of automation code for retries and err handling etc. yourself. . .&lt;/p&gt;

&lt;p&gt;But I'm sure you'd rather keep yourself busy with doing cool new algorithms or functional code, rather than get mired down in automation tasks!&lt;/p&gt;

&lt;h2&gt;
  
  
  Free &amp;amp; Open-source
&lt;/h2&gt;

&lt;p&gt;The source code is here: &lt;a href="https://github.com/pypyr/pypyr/" rel="noopener noreferrer"&gt;https://github.com/pypyr/pypyr/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to contribute, even just a GitHub star is useful! &lt;/p&gt;

&lt;p&gt;I wrote the 1st code for this years ago when I found myself having to solve the same sort of problems each time I wrote some sort of automation script. . . Over the years it's grown as I've found more &amp;amp; more uses for it and expanded upon it, and because it was open-source from day one &lt;a href="https://pypyr.io/" rel="noopener noreferrer"&gt;pypyr&lt;/a&gt; has been blessed with great enhancements from superb contributors over the years who brought ideas, enhancements and fixes that I wouldn't even have thought of. Big shout out to you all!&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://pypyr.io/docs/" rel="noopener noreferrer"&gt;pypyr technical documentation website&lt;/a&gt; source-code itself is also open-source, and packaged as a re-usable Hugo static site generator theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  100% Test Coverage
&lt;/h2&gt;

&lt;p&gt;From the beginning I've made an effort to keep 100% test coverage over both unit &amp;amp; integration tests, and this (although annoying to keep to that sometimes. . .) has been invaluable in preventing regressions &amp;amp; providing steady stability of the tool as it evolved, but let me not break my arm patting myself on the back here. . . this implementation detail is probably not all that interesting unless you're into code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback &amp;amp; Help
&lt;/h2&gt;

&lt;p&gt;Feedback, comments and/or ideas to make things better on pypyr itself and also the new &lt;a href="https://pypyr.io/" rel="noopener noreferrer"&gt;pypyr documentation website&lt;/a&gt; are very welcome. . .&lt;/p&gt;

&lt;p&gt;Also, if you have questions, feel very free to ask, I'm here to help!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>productivity</category>
      <category>devops</category>
      <category>python</category>
    </item>
  </channel>
</rss>
