Welcome to runif’s documentation!

Welcome to Run…if

Runif is an idempotent and minimal python 3 library for rapid scripting. Provide support for creating file, adding data to them, patching and so on.

run if in a slogan: - Execute idempotent scripts based on file contents. - Battle tested on over 60 conversions - No dependency: it works with python library out of the box

Simple usage: first example

from runif import *

def demo_python_builder(fname):
    with open(fname,"w") as f:
            "def testme(str):\n",
            "\tprintln('Message from "+fname+" '+str)\n",

run_if_missed("workdir", lambda dir_to_build:  (Path(dir_to_build)).mkdir() )
for x in range(4):
    run_if_missed("workdir/demo"+str(x)+".py", demo_python_builder)

Create workdir if missed.

Complex example

zconnect2Gradle.py is a complex example we used on a production site.

We need a way to convert several (hundreds) of IBM ZConnect project(s) to Gradle. Source project was done on Eclipse via a propietary plugin. The following script will read the provided directory, added a small gradle property file and collect all the data in a master yaml file called “recap”.

#!/usr/bin/env python
# Python3 dep wizard

import hashlib
import sys
import functools, re
import zipfile, os

import click

from runif import *

# Example of a fragment added to the recap
# See https://pyformat.info/
# '{first} {last}'.format(**data)
DEP_DESC="""    {appName}:
      application_name: {appName}
      component_type: 'zconnect_{appType}'
      group_id: 'my.shiny.project'
      artifact_id: '{artifactId}'
      version: '1.0.0-SNAPSHOT'

# aar for api and sar for sevices
def create_gradle_prop(projectName, propfile):
    print("Creating",propfile, "for", projectName)
    # Compute application name
    if "-API" in projectName:
    actualized_props=PROP_TEMPLATE % (appName, appType)
    with open(propfile,"w") as f:
    data={ "appName": appName.replace('-','_'), "appType": appType, "artifactId":appName}
    dep_desc_fragment=DEP_DESC.format(**data )
    append_if_missed("recap.yml", dep_desc_fragment)

def create_master_recap(fname):
    with open(fname,"w") as f:
  # Describe ZConnect deploy

def process(project):
    run_if_missed("recap.yml", create_master_recap)
    run_if_missed(project+"/gradle.properties", lambda f: create_gradle_prop(project, f))

@click.argument("projectdirs", nargs=-1)
def zconnect2gradle(projectdirs):
    Given a set of Zconnect directories
    Create a gradle.properties for each project and try also to build a deployment descriptor.

    Version are fixed to 1.0.0-SNAPSHOT and group is fixed to my.shiny.project
    It is able to detect sar or aar via naming convention.
    Bring the entire project name as artifact name    
    for project in projectdirs:

if __name__=="__main__":

Runif functions

exception runif.RunifError

Raised by run function if the called command has a return code != 0.

runif.append_if_missed(fname, *args)

You pass a list of strings. For every string, if it is not already present on fname, it is appended.

runif.extract_var(fname, var_name)

Extract a var from a simple property file (with assignments). Property files are like Java property files with syntax:


It can be useful to extract data from gradle.properties, spring properties or so on

runif.regexp_replace_file(filename: str, compiled_regexp, replace_str: str)

Smart way of replacing a compiled regexp in a file Rewrite the file only if really needed

runif.run(cmd, extra_param=None)

Supports one extra quoted params in append

runif.run_each(path: str, glob: str, func)

Scan files and run in a sequential fashion. The func must return True if make some changes

runif.run_each_async(path: str, glob: str, func, pool_size: int = 1)

Scan files and run in a multi-process fashion. The func must return True if make some changes

The function must AVOID calling not hread-safe function like run_if_modified

Spawn overhead is low but parallelism is not high because of ThreadPoolExecutor

runif.run_if_missed(fname: str, funx)

If fname is missed, execute funx(fname) Return True if call the function, False otherwise.

runif.run_if_modified(fname: str, funx, cache_file='.runif_cache')

Run the function if fname is modified with respect of last run. To check for modification a md5 signature is used. The signature are stored in a local cache_file the caller can customize. It works well with small files, it can be slow on larger one.

runif.run_if_present(fname: str, funx)

If fname is present, execute funx(fname) Return True if call the function, False otherwise.

runif.run_if_unmarked(fname, marker, fun_to_call_if_unmarked)

Run the function if the string marker is not found inside file fname Marker can be substring of a line. Tipical usage secnario is to run a function which modify the content of a file.

The function will be called with two parameters: filename and marker so it can handle more than one function type.

Indices and tables