Skip to content

Developer Guide

Jennings Zhang edited this page Dec 3, 2021 · 12 revisions

Plugin Developer Guide

This document explains two mechanisms for running and further developing your plugin. The first is using a local container build, the second is to directly install to a local python virtual environment.

Where do I put my code?

For a ChRIS plugin named pl-myproject, add the code to the generated file myproject/myproject.py. Near the end of the file you will find the functions

  • define_parameters(self): specify input variables here

  • run(self, options): functionality goes here

Refer to pl-simpledsapp as an example.

Tip
Large scripts can be organized into new, separate files which define functions or classes that can be called by the run method of myproject/myproject.py.

What are these other files?

Table 1. Project Tree
File Name Purpose

myproject/myproject.py

Main source code for your app, the entrypoint.

myproject/__init__.py

Python module boilerplate (don’t worry about it).

myproject/__main__.py

Python script boilerplate (don’t worry about it).

Dockerfile

Defines how to build a container image of your app. What is Docker?

.github/workflows/ci.yml

Automates testing and building, aka "continuous integration" (don’t worry about it).

.gitignore

Prevents private or temporary files from being uploaded publicly when you push to Github. .gitignore

LICENSE

Legal document. The default license is "MIT License," which is simple and free.

README.rst

Introductory documentation. Please keep this up to date and write thoroughly!

requirements.txt

Lists exact Python library dependencies.

setup.py

Describes meta-information (such as project name, title, author) and how the Python package manger pip should install your application.

Local Development (and debugging)

We recommend running your plugin only from a docker image, and not on the metal (i.e. without docker). Certainly in this manner you completely replicate the execution environment of your application and can immediately determine lost or implicit dependencies and just buffer your application from a possibly idiosyncratic host environment. Afteral, the plugin is supposed to be run as a container, so it is really best practice to develop and debug it containerized.

We do recognize that in some cases, however, running quickly on the metal might be desired. In that case, the installation vector is using pip install . to a local python virtual environment.

Remember that ChRIS plugin code as created by the cookiecutter is really only a module or a stub on your system. Though you might have edited the actual app.py code, this code is not runnable just as is. The act of creating a fully fledged ChRIS plugin is reliant on installing the application correctly, in other words, creating the driving script that uses your module.

In order to create the final working script, we leverage the Python setup.py infrastructure to automatically create and install this script for you. In the case of the containerzied build, this script is created within the container. If you leverage setup.py directly on your system, the script is created in your python virtual environment.

Using docker to build and run a local container

Build a local version of your plugin. The Dockerfile created by the cookiecutter contains some example snippets to this effect. Essentially all you do is (obviously replace pl-app appropriately):

docker build -t local/pl-app:1.0.0 .

(alternatively, you can build a version and push to dockerhub: — see here.)

The developer workflow mostly requires only one single local build. Subsequent edits to your source plugin do not require a container rebuild provided you volume map your application into the created container. In this manner, you build a container, run it once or twice, then volume map your module app into the container and continue developing:

In the following, appname of course refers to the plugin name. Obviously use an appname applicable to your context and replace [<cliflags>] with flags germane to your use case.

docker run --rm -ti -u $(id -u)                                              \
           -v $PWD/appname:/usr/local/lib/python3.9/site-packages/appname:ro \
           local/pl-appname appname [<cliflags>] /incoming /outgoing
  • :ro mounts the volume as "read-only," files cannot be changed from within the container

  • -u $(id -u) sets that you will own the files produced by running the app

It may be useful to start a container as an interactive shell instead of running the app:

docker run --rm --name=my-plugin-shell -u $(id -u)                             \
           -v $PWD/appname:/usr/local/lib/python3.9/site-packages/appname:ro   \
           -it --entrypoint /bin/bash                                          \
           local/pl-appname

In a second terminal window, run

docker exec -it -u root my-plugin-shell /bin/bash

To launch a second interactive shell inside the same container, but as the admin user so that you can install additional packages like

apt update && apt install inetutils-ping vim

Logging Timestamps

If you add logging to your app which prints timestamps, they may seem off.

The timezone inside of containers defaults to UTC. You can change this by mounting your host’s timezone configuration.

docker run -v /etc/localtime:/etc/localtime:ro ubuntu date

Python Without Docker

Important
without containerization, reproducibility is not guaranteed!

Your on-the-metal environment must in some way mimic what’s in Dockerfile. Essentially, you’ll need to pip install your app.

It is recommended to use venv.

python -m venv venv && source venv/bin/activate  # optional
pip install -e .
appname --man

Can I automate testing?

Yes! Automatic tests are very important and greatly improve the quality of your software.

Why Do I Get Emails About "Run Failed: CI"?

cookiecutter-chrisapp produces boilerplate (aka starter code) for writing unit tests. These are run on Github after every git push.

You should write proper tests for your ChRIS plugin. However, you may also simply disable testing. See Automatic Tests#22-disable-automatic-testing

My Automatic Build Is Stuck or Crashing After Hours

The automatic build is configured by default to target multiple architectures:

  • x86_64 (amd64, "native" for most computers)

  • PowerPC (ppc64le, used by supercomputers in the MOC)

  • ARM64 (arm64, native for upcoming Apple Macs, and most smartphones)

x86_64 builds are usually seamless and work as-expected. Unfortunately, there are discrepancies between the architectures. "Automatic" does not mean "without effort." Successful cross-compilation requires some effort to understand these discrepancies.

Giving Up on Automatic Builds

You can change which platforms your build targets. Instructions in Automatic Builds