Advanced Features#

In this section, advanced ISON language features are described.

Runtime Variables#

These types of variables are declared in a dictionary block with the tag __runtime_vars__. Runtime variables are available just like global variables. However, they are not stored in the resultant dictionary itself, but only in the parser instance. This can be useful, if you need to read in a large file but only pick a small part from this file per configuration. In this case, you don’t want to copy the full data set to the resultant dictionary.

import ison

dicData = {
    "__runtime_vars__": {
        "my_large_data": "$range{100}",
    },

    "iResult": "${my_large_data:40}",
}

dicResult = ison.run.Run(xData=dicData, bStripVars=False)
print(ison.run.ToString(dicResult))
{
    "iResult": 40,
    "__globals__": {},
    "__func_globals__": {}
}

Including Files#

Additional ISON or JSON files can be included using the __includes__ language feature. This must be the key of a JSON dictionary, and its’ value must be a list of strings, specifying file paths. If the filepaths are not absolute paths, they are regarded relative to the include statement’s file. The parser will ensure that cyclic includes are detected and cause an error.

The __includes__ element of a dictionary causes the parser to load all files in the order given in the list and update the parent dictionary with the elements of the loaded JSON files. The processing order is as follows:

  1. If an __includes__ element is defined, load all includes and update the current dictionary.

  2. All variables declared in the dictionary are processed.

  3. If any of the variable declaration dictionaries hase an __includes__ element these are included and applied to the respective variable dictionary.

  4. Any variables added by including the additional files are processed.

This means,

  • you can add an __includes__ statement inside a variable declaration dictionary like __globals__ to load additional variable definitions,

  • if you are using ISON variables in the include file paths, they must have been defined outside the current dictionary.

Here are some examples.

import ison
from pathlib import Path

dicData = {
    # This loads the file 'demo.json' from the same folder.
    # 'demo.json' contains this:
    # {
    #     "hello": ["world", 1]
    # }

    "__includes__": ["demo"],
}

dicResult = ison.run.Run(xData=dicData, bStripVars=True, sImportPath=Path.cwd())
print(ison.run.ToString(dicResult))
{
    "hello": [
        "world",
        1
    ]
}

In the following example, the ‘demo.json’ file is used as local variables definition.

import ison
from pathlib import Path

dicData = {
    # This loads the file 'demo.json' from the same folder.
    # 'demo.json' contains this:
    # {
    #     "hello": ["world", 1]
    # }
    "__locals__": {
        "__includes__": ["demo"],
    },
    "result": "$hello",
}

dicResult = ison.run.Run(xData=dicData, bStripVars=False, sImportPath=Path.cwd())
print(ison.run.ToString(dicResult))
{
    "__locals__": {
        "hello": [
            "world",
            1
        ]
    },
    "result": [
        "world",
        1
    ],
    "__globals__": {},
    "__func_globals__": {}
}

Using a variable in an include path.

import ison
from pathlib import Path

dicData = {
    "__locals__": {
        "path": "demo.json",
    },

    "data": {
        "__includes__": ["$path"],
    },
}

dicResult = ison.run.Run(xData=dicData, bStripVars=False, sImportPath=Path.cwd())
print(ison.run.ToString(dicResult))
{
    "__locals__": {
        "path": "demo.json"
    },
    "data": {
        "hello": [
            "world",
            1
        ]
    },
    "__globals__": {},
    "__func_globals__": {}
}

Platform Dependent Dictionary#

Sometimes variables of parameters need to be specified depending on the current platform or machine the ISON file is processed on. This can be achieved with the __platform__ element. The pseudo structure of platform dictionary is:

{
    "__platform__": {
        "[Windows|Linux]": {
            "__data__": {
                // data for the respective system
            },
            "[Node name]": {
                "__data__": {
                    // data for node of respective operating system
                }
            }
        }
    }
}

Elements defined in a __data__ dictionary for a node overwrite elements defined in the __data__ dictionary dor the operating system. These in turn overwrite elements in the parent dictionary of the __platform__ key. In this way, you can either specify data for a specific operating system or even a specific node (computer) running this operating system. Note that if a __data__ dictionary is defined for a node, the __data__ dictionary for the operating system is optional.

Note

The platform element is processed before any other element of the dictionary. After it is processed, the whole platform dictionary is removed and won’t appear in the result dictionary.

One use-case is specifying absolute data paths per machine.

import ison

dicData = {
    "__locals__": {
        # Default value for path
        "path": "unknown",

        "__platform__": {
            "Windows": {
                "__data__": {
                    # default value for all windows machines
                    "path": "demo",
                },
                "my-laptop": {
                    "__data__": {
                        # value for windows machine 'my-laptop'
                        "path": "C:/users/foo/my-data.json"
                    }
                }
            },
            "Linux": {
                "__data__": {
                    # Default value for all Linux machines
                    "path": "demo2",
                },
            }
        }
    },

    "result": {
        "__includes__": ["$path"]
    }
}

dicResult = ison.run.Run(xData=dicData, bStripVars=False, sImportPath=Path.cwd())
print(ison.run.ToString(dicResult))
{
    "__locals__": {
        "path": "demo"
    },
    "result": {
        "hello": [
            "world",
            1
        ]
    },
    "__globals__": {},
    "__func_globals__": {}
}

Wildcards in Node Names#

The node names can also include wildcards:

  • *: any number of characters (also none)

  • ?: exactly one character

For example:

import ison

dicData = {
    "__locals__": {
        # Default value for path
        "path": "unknown",

        "__platform__": {
            "Windows": {
                "__data__": {
                    # default value for all windows machines
                    "path": "demonstrate",
                },
                "??-C-*": {
                    "__data__": {
                        # value for windows machine with name '??-C-*'
                        "path": "demo"
                    }
                }
            },
            "Linux": {
                "__data__": {
                    # Default value for all Linux machines
                    "path": "demo2",
                },
            }
        }
    },

    "result": {
        "__includes__": ["$path"]
    }
}

dicResult = ison.run.Run(xData=dicData, bStripVars=False, sImportPath=Path.cwd())
print(ison.run.ToString(dicResult))
{
    "__locals__": {
        "path": "demo"
    },
    "result": {
        "hello": [
            "world",
            1
        ]
    },
    "__globals__": {},
    "__func_globals__": {}
}

Logging & Print Debugging#

Sometimes it is helpful to print the state of variables during parsing, or to log parsed values. This can be done with the functions $print{} and $set-log-path{}. If no logging path of file is defined with the latter, the former prints to stdout. If a path to a logging file is set, $print{} writes to the log file.

import ison

dicData = {
    "__locals__": {
        "lA": ["Hello", "World"],
        "dicA": { "Hello": "World" },
        "sLog": "$print{Variable 'dicA':, $json{$dicA}, $lA}"
    },
    "result": "$dicA"
}

try:
    dicResult = ison.run.Run(xData=dicData, bPrintWarnings=True)
    print(ison.run.ToString(dicResult))

except Exception as xEx:
    print(str(xEx))
# endtry
Variable 'dicA':
{
    "Hello": "World"
}
['Hello', 'World']
{
    "result": {
        "Hello": "World"
    }
}

You can set a logging path in three ways with $set-log-path{}:

  1. Without arguments the current working directory is used a logging path and a filename is automatically generated.

  2. Give a path without filename. In this case, a filename is automatically generated and the path is created.

  3. Give a full filepath. If the path does not exist, it is created.

import ison

dicData = {
    "__locals__": {
        "sLogPath": "$set-log-path{}",
        "dicA": { "Hello": "World" },
        "sLog": "$print{Variable 'dicA':, $json{$dicA}, }"
    },
    # Uncomment the following line to see the path of the log file
    # in the printed output.
    # "log-path": "${sLogPath}",
    "result": "$dicA"
}

try:
    dicResult = ison.run.Run(xData=dicData, bPrintWarnings=True)
    print(ison.run.ToString(dicResult))

except Exception as xEx:
    print(str(xEx))
# endtry
{
    "result": {
        "Hello": "World"
    }
}