Developer Documentation

Setting up an Environment for pyMOR Development

Getting the source

Clone the pyMOR git repository using:

git clone pymor_source
cd pymor_source

and, optionally, switch to the branch you are interested in, e.g.:

git checkout 2020.2.x

Environment with Virtualenv

Create and activate a new virtualenv:

python3 -m virtualenv venv
source venv/bin/activate

Then, make an editable installation of pyMOR with:

pip install -e '.[full]'

Environment with docker-compose

To get a shell in a preconfigured container run (if required set PYMOR_SUDO=1 in your environment to execute docker with elevated rights):

make docker_run

You can also use the setup in .binder/docker-compose.yml to easily work on pyMOR with pyCharm.

Coding guidelines and project management

Code style

pyMOR follows the coding style of PEP8 apart from a few exceptions. Configurations for the PEP8 and flake8 code checkers are contained in setup.cfg.

As an additional rule when calling functions, positional arguments should generally be passed as positional arguments whereas keyword arguments should be passed as keyword arguments. This will make your code less likely to break, when the called function is extended.

All functions and classes called or instantiated by users should be sufficiently well documented.

How to check code style

Firstly, make sure that you installed the requirements-ci.txt with pip install -r requirements-ci.txt. Afterwards use the Makefile to check for flake8 warnings with make flake8 or directly use flake8 on the src folder.

GitHub project

All new code enters pyMOR by means of a pull request. Pull requests (PR) trigger automatic tests which are mandatory to pass before the PR can be merged. A PR must also be tagged with either one of the pr:new-feature, pr:fix, pr:removal, pr:deprecation or pr:change labels to clearly identify the type of changes it introduces. See the labels’ descriptions for more details.

pre-commit hooks

pyMOR ships a config for the pre-commit hook management system. Using this setup can be a good way to find flake8 errors before pushing to GitHub, but using it is not required. Once you have pre-commit installed in you environment, run

pre-commit install

Afterwards, the hooks configured in .pre-commit-config.yaml will run on all changed files prior to committing changes. Errors will block the commit, some checks will automatically fix the file.

pyMOR’s dependencies

The single source of truth for our dependency setup is From it the requirements*txt files and pyproject.toml are generated (by calling make). During execution is imported and the package lists passed to setuptools’s setup_requires, install_requires, tests_require and extra_requires accordingly (see this list). The extra_requires dictionary here controls what ‘extra’ configurations are available for pip install pymor[extra1,extra2,extra3]. The requirements files are input for .ci/ which created a Conda Environment spec file that contains only those dependencies that are available on all OS-Python combinations for which GitHub Action based CI is run in Conda envs.

A GitHub Action will update all “downstream” files of if it changes in a push. The result will be pushed into the same branch, but due to a GitHub limitation no new workflows will run as a result of that push.

When adding new package dependencies, or version restrictions, these need to be reflected into a commit in our docker repository for the constraints requirements so that updated images become available to CI after entering the new commit hash into .env.

The Makefile

Via the Makefile it is possible to execute tests close to how they are run on CI with make docker_test. All jobs described in Gitlab CI Test Stage can be run this way by setting PYMOR_TEST_SCRIPT accordingly. You can pass additional arguments to pytest by setting PYMOR_PYTEST_EXTRA. To run the test suite without docker, simply execute make test in the base directory of the pyMOR repository. This will run the pytest suite with the default hypothesis profile “dev”. For available profiles see src/pymortests/ A profile is selected by running make PYMOR_HYPOTHESIS_PROFILE=PROFILE_NAME test. Run make full-test to also enable pyflakes and pep8 checks.

the .env file

This file records defaults used when executing CI scripts. These are loaded by make and can be overridden like this: make DOCKER_BASE_PYTHON=3.8 docker_test (see also the top of the Makefile).

Continuous Testing / Integration Setup

Our CI infrastructure is spread across two major platforms. These are Gitlab CI (Linux testsuite) and GitHub Actions (Conda-based MacOS and Windows testsuite, misc. checks).

pyMOR uses pytest for unit testing. All tests are contained within the src/pymortests directory and can be run individually by executing python3 src/pymortests/ or invoking pytest directly. Please refer to the pytest documentation for detailed examples.

Gitlab CI


Configured by .ci/gitlab/ci.yml which is generated from .ci/gitlab/ by the calling make template (needs appropriate Python environment) or make docker_template.

All stages are run in docker containers (more info). Jobs that potentially install packages get a frozen pypi mirror as a “service” container. The mirror has a “oldest” variant in which all requirements are available in the oldest versions that still satisfy all version restrictions (recursively checked).

Stage: Sanity

A smoke test for the CI setup itself. Checks if the can be processed and if all docker images needed by subsequent stages are available in the registry. Also ensures CI config and requirements generated from their templates match the committed files.

Stage: Test

This stage executes ./.ci/gitlab/test_{{script}}.bash for a list of different scripts:


This runs plain pytest with the common options defined in ./.ci/gitlab/common_test_setup.bash.


Builds and executes the minimal cpp demo in src/pymordemos/minimal_cpp_demo/, see also Tutorial: Binding an external PDE solver to pyMOR.


Runs all demos with mpirun -np 2 via src/pymortests/, checks against recorded results.


Same as vanilla, but against the unreleased numpy development branch. This makes sure we catch deprecation warnings or breaking changes early.


Same as vanilla, but installs only packages from the “oldest” pypi mirror.


First install pyMOR from git over https, uninstall and then install with pip install .[full]. Uninstall again and install from a generated (and checked) sdist tarball. Lastly run the pytest suite on the installed (!) pyMOR, not the git working tree.


Using docutils magic this extracts the Python code from all the tutorials in docs/source/tutorials_* (except tutorial_external_solver since that needs kernel switching) and runs it in parameterized pytest fixtures as imported modules.

All scripts are executed for all Python versions that pyMOR currently supports, with the exception of numpy_git and oldest. These are only tested against the newest and oldest versions accordingly.

Stage: Build

Builds documentation and manylinux wheels on all supported pythons. Also builds and pushes a docker image that includes pyMOR installed from checkout. This is used as the base image for the binder-ready deployment of the documentation in the last stage.

Stage: Install_checks

from wheel

Try to install wheels produced in previous stage on a few different Linuxs.

from source

Try to install pymor[full] from git checkout. This checks that the extension module compile works, which is not covered by the “from wheel” step. Also install full optional requirements, which include packages omitted from [full], after necessary additional system package install.

local docker

Ensures minimal functionality for the local docker development setup .

Stage: Deploy


Commits documentation built in Stage: Build (from a single Python version, not all) to the documentation repository. This repository is the source for served via GitHub Pages. A binder setup for the generated tutorials notebooks is added on a branch with a name matching the currently checked out git branch of pyMOR.


Upload wheels to either the test or the real instance of the pypi repository, depending on whether the pipeline runs for a tagged commit.


This job accumulates all the coverage databases generated by previous stages and submits that to

Github - Gitlab bridge

This a sanic based Python application that receives webhook events from GitHub for pull requests and pushes PR branches merged into main to Gitlab to run a parallel CI pipeline to check whether the main branch will still pass tests after the PR is merged. The bridge also does this for forks of pyMOR, but these have to be whitelisted in order to protect CI secrets.

GitHub Actions


Configured by individual files in .github/workflows/*

  • Check all (external) links in changed Markdown documents are accessible.

  • Make sure at least one pr:* label is set on the PR.

  • Prohibit any commits with messages that indicate they can be auto-squashed

  • Auto-assign the labels if certain files are changed by the PR.

  • Update requirement files / conda env if changes.

  • Runs pytest in conda-based environments on Windows/MacOS/Linux for oldest and newest supported Pythons

    • Entire conda envs are cached and only update if either manually invalidated by incrementing CACHE_NUMBER or if dependencies change.

    • All pytest jobs export a full environment lockfile, which can be downloaded on the summary tab for the “Conda Tests” action. Look for “Conda Env Exports”.

    • Pytest XML reports can also be found there.

Docker images

The source for most of our docker images is this repository. The images are build by a Makefile system that expresses dependencies, handles parameterization, preloads caching and so forth. Builds are only permitted on a clean working tree, to increase reproducibility. On each push GitLab CI builds the entire tree. Great effort went into making incremental updates as fast as possible, but full rebuilds will take upwards of 3 hours. There are two basic categories for images: those that get generated for each supported Python version and those that are version independent. For CI the main image, in which the pytest suite runs, is defined in testing/. The main workflow for this repository is adding new packages to the appropriate requirements file in the constraints/ subdir. From there those packages will become available in the pypi_mirror-* images, but also pre-installed in the testing image.