CPython

Normally when you use Python on your computer you are using CPython. However, the default Python support in RiskScape (Jython) behaves a little differently, particularly once you move beyond simple RiskScape Python functions.

RiskScape’s CPython integration lets you write RiskScape functions that use the exact same Python installation as you use normally.

Note

To use CPython functions in RiskScape, you must first have CPython installed locally on your system.

Configuring CPython support in RiskScape

Normally when you run Python code outside of RiskScape, you will be using a python binary (Linux) or a python.exe executable (Windows) that is installed somewhere on your system.

In order to use CPython in RiskScape, you have to first tell RiskScape where this Python binary is located. We do this by specifying full path to the Python binary in RiskScape’s settings INI file.

You may need to create the settings.ini file if it does not already exist. On Windows, you can do this by entering the following commands into your terminal:

mkdir %USERPROFILE%\RiskScape
notepad %USERPROFILE%\RiskScape\settings.ini

Next, you will need to add a [cpython] configuration section and specify the python3-bin path to the Python binary. On Windows, a typical settings.ini might look something like this:

[cpython]
python3-bin = C:\Users\Ronnie\AppData\Local\Python\Python 3.8\python.exe

The actual python3-bin path will vary depending on where Python was installed on your system. If you are not sure where Python is installed on your system, try running:

where python

Note

If you use several different Python environments (i.e. venv), then each virtual environment will have its own Python executable. You will need to activate the Python environment you want to use first, before running where python.

For example, if you use the Anaconda Python distribution the default Python location might look like C:\Users\Ronnie\Anaconda3\python.exe. Whereas the path for a Python environment called Geo would look more like C:\Users\Ronnie\Anaconda3\envs\Geo\python.exe.

On Linux or Mac, the settings.ini file will look more like the following (note that the path may vary).

[cpython]
python3-bin = /usr/bin/python3

You can test your installation by running riskscape -V. If any warnings or errors occur, then you may have specified the incorrect python3-bin file path. Try following the Troubleshooting section below.

Writing CPython functions for RiskScape

There is no special magic required to make your functions work with RiskScape, but there are a few important points to know.

  • You must have a function in your script called function - this is the one that RiskScape will call.

  • To import a Python package in your RiskScape function, you must first have that CPython package installed locally. Refer to python.org for more details on how to install a Python package.

  • Data is passed to CPython in simple JSON-like data types; it will be a mixture of numbers, strings, lists and dictionaries. Trying to use more complicated data types, like coverages or lookup tables, will fail.

  • You can use print for debugging your scripts within RiskScape - the output is sent to RiskScape and will be displayed alongside any other RiskScape output.

Note

Your function will get called for every element-at-risk, so adding print statements can result in a lot of output for riskscape model run commands. We recommend only using print with riskscape expression evaluate, or with a small sub-set of model data.

  • You can test your functions with and without RiskScape. You could use a separate python script that imports and tests your CPython RiskScape function script, or you can test it in RiskScape using the riskscape expression evaluate command.

  • For more efficient model processing with CPython, it can be worth defining the type that your function uses. A lot of the examples will simply use argument-types = [ building: anything, hazard: nullable(floating) ]. However, this can be inefficient because the anything type will pass all the exposure-layer data to your function, regardless of whether the function uses it or not.

  • Not all data types are supported with CPython. For example, decimal and date types are currently not supported. This will result in an error if your exposure-layer data contains these types. You can avoid this problem by defining the exact type that your function expects, instead of building: anything.

A Simple example

To start with we’ll create a simple hello world python function. Create a helloworld.py file in your favourite text editor and add the following:

def function(name):
  return "Hello, %s" % name

In the same directory, create a project.ini and add the following:

[function helloworld]
framework = cpython
location = helloworld.py
return-type = text
argument-types = [text]

Now, in the same directory, open a terminal or command prompt and test your function with the following command:

riskscape expression eval "helloworld('Ronnie')"

If it has worked successfully, you should see:

Hello, Ronnie

Fortunately, CPython integration is not limited to just “Hello, world” examples.

Using Geometry in your CPython functions

RiskScape supports passing geometry to your CPython function, but there are some important things you will need to know if you want to manipulate geometry in your Python code.

There are many Python packages available for working with geometry. RiskScape can’t predict what specific geometry package you will want to use, so it passes the geometry to your function in the well-known bytes format (known as WKB).

In your function, you can then construct a geometry object from the WKB. How you do this depends on what Python package you are using for geometry.

If your function also returns geometry, then you will need to return it in WKB format, rather than returning the geometry object itself.

Here is a simple example that uses the shapely Python package to work with geometry. The following function both takes geometry as an argument and also returns it.

# project.ini
[function get_centroid]
location = centroid.py
framework = cpython
description = Return the centroid (point) of a simple feature
return-type = geometry
argument-types = [geometry]
# centroid.py
from shapely import wkb

# returns the centroid of the feature
def function(geom):
  # note the geometry argument gets passed as a tuple
  bytes, srid = geom
  # use shapely to turn the WKB into geometry
  shapely_geom = wkb.loads(bytes)

  # return the input geometry's centroid (as WKB)
  return (shapely_geom.centroid.wkb, srid)

Notice that the geometry argument is passed as a two-item tuple, containing the WKB bytes as well as an srid. This is because RiskScape stores an opaque identifier, called an spatial reference identifier (SRID), to identify the coordinate reference system the geometry belongs to.

Tip

You only need to worry about the SRID if your function’s return-type contains geometry, i.e. you want to modify the geometry yourself in Python. Returning the same SRID tells RiskScape that the coordinate reference system has not changed.

CPython vs Jython

The CPython and Jython plugins are both enabled in your RiskScape installation by default. If you correctly setup CPython in your settings.ini, any Python functions in your project will be executed using CPython by default. Otherwise, Jython will be used by default.

In your INI file, you can explicitly define what Python implementation RiskScape should use via the framework parameter, like we did in our helloworld example earlier. This makes it possible to use a mix of both Jython (i.e. framework = jython) and CPython (i.e. framework = cpython) functions in your project.

Whether you use CPython or Jython will depend on your circumstances. Using CPython requires extra setup, as you need to have a pre-existing working python environment available, but it gives you access to a complete and familiar python environment.

Jython requires no setup, but is limited to python 2.7 and is geared towards scripting a Java environment, rather than being a true python environment. For simple functions this isn’t a problem.

Tip

If you have never used Python before, and only want to write a simple risk function, then we recommend using Jython. If you want to use other Python modules, such as numpy then you should use CPython.

Troubleshooting

First, check the python3-bin path in your settings.ini is a valid Python 3 executable using the python -V command.

C:\Your\Path\To\Python\python3.exe -V

RiskScape only supports Python 3. If you see a 2.x.x version displayed, then you will need to install Python 3. You may also want to check your version of Python 3 is still supported. Older, unsupported versions of Python 3 may still fail to work with RiskScape due to compatibility issues. RiskScape is known to work with Python v3.8 and v3.9.

Next, check that you can use your python.exe path to execute a simple Python statement, e.g.

C:\Your\Path\To\Python\python.exe -c "print(1 + 1)"
2

If you see an error instead of 2, it may mean you are using the wrong python.exe path, or you need to install a more recent version of Python on your system. For example, a bad Python install might throw out a cryptic error like:

C:\Your\Path\To\Python\python.exe -c "print(1 + 1)"
ImportError: No module named site

Finally, if that all works, check that all the settings.ini keywords are all lower-case, and RiskScape is loading the correct settings.ini file. The following command will display debug containing the settings.ini and CPython path.

riskscape --log-level .engine.cli.CliBootstrap=info,.cpython=info --version

You could also double-check the INI file definition for your function. If it contains the framework parameter, then check it’s set to the Python implementation that you want to use, i.e. cpython or jython.