Configuration options

All Nengo Bones configuration is done through the .nengobones.yml file. In this notebook we will demonstrate the different configuration options available and how to use them to customize behaviour for different use cases.

First we’ll define some utility functions we’ll use throughout this notebook:

[1]:
import os

import nengo_bones


def write_yml(contents):
    """Create a sample .nengo.yml file from a string."""

    nengo_yml = "project_name: Example Project\n"
    nengo_yml += "pkg_name: eg_package\n"
    nengo_yml += "repo_name: eg-org/eg-package\n"
    nengo_yml += contents

    with open(".nengobones.yml", "w") as f:
        f.write(nengo_yml)


def display_contents(filename, sections=None):
    """
    Display the contents of a file.

    The 'sections' argument filters the file to show only the
    specified sections.
    """

    with open(filename) as f:
        data = f.readlines()

    # strip out blank lines for .sh and .yml files
    if filename.endswith(".sh") or filename.endswith(".yml"):
        data = [x for x in data if x.strip() != ""]

    if sections is None:
        display_data = data
    else:
        # pull out only the requested sections
        display_data = []
        for sec in sections:
            for i, line in enumerate(data):
                if ('"$COMMAND" == "%s"' % sec in line
                        or line.startswith(sec)):
                    section_data = data[i:]
                    break
            for i, line in enumerate(section_data[1:]):
                if filename.endswith(".cfg"):
                    if line.startswith("["):
                        section_data = section_data[:i+1]
                        break
                else:
                    if not line.startswith(" "):
                        section_data = section_data[:i+1]
                        break
            display_data.extend(section_data)

    print("".join(display_data).rstrip("\n"))

General options

There are a few general configuration options that affect multiple templated files:

  • project_name: Natural-language name for the project (e.g., “Nengo Bones”).

  • pkg_name: Import name for the project (e.g., “nengo_bones”).

  • repo_name: Github organization/repository name (e.g., “nengo/nengo-bones”).

  • author: Name of project author (defaults to “Applied Brain Research”).

  • author_email: Email of project author (defaults to “info@appliedbrainresearch.com”).

  • description: A brief, one-sentence description of the project.

  • copyright_start: The year the project was first publicly available (defaults to current year).

  • copyright_end: The last year work was done on the project (defaults to current year).

CI scripts

Nengo Bones uses a continuous integration (CI) setup wherein build jobs are associated with different shell scripts, and each script defines commands to be executed at different stages during the build process. These scripts are configured through the ci_scripts section of .nengobones.yml. The basic step is to add an entry with - template: scriptname for each script template that we want to be rendered. As an example, in this section we will use the “test” template, but all the options we describe here work for any of the templated scripts.

Normally the .nengobones.yml file is a text file sitting in the root directory of the repository. For demonstration purposes, in this notebook we will be generating different config files on-the-fly using the utility functions from above.

[2]:
nengobones_yml = """
ci_scripts:
    - template: test
"""
# create .nengobones.yml file
write_yml(nengobones_yml)
# call the bones-generate script
!bones-generate ci-scripts
# display the contents of the generated file
display_contents("test.sh")
#!/usr/bin/env bash
# Automatically generated by nengo-bones, do not edit this file directly
NAME=$0
COMMAND=$1
STATUS=0  # used to exit with non-zero status if any command fails
exe() {
    echo "\$ $*";
    # remove empty spaces from args
    args=( "$@" )
    for i in "${!args[@]}"; do
      [ -n "${args[$i]}" ] || unset "args[$i]"
    done
    "${args[@]}" || { echo -e "\033[1;31mCOMMAND '${args[0]}' FAILED\033[0m"; STATUS=1; }
}
if [[ ! -e eg_package ]]; then
    echo "Run this script from the root directory of this repository"
    exit 1
fi
if [[ "$COMMAND" == "install" ]]; then
    :
    exe pip install "pytest>=3.6.0"
    exe pip install "pytest-xdist>=1.16.0"
    exe pip install -e ".[tests]"
elif [[ "$COMMAND" == "before_script" ]]; then
    :
elif [[ "$COMMAND" == "script" ]]; then
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS
elif [[ "$COMMAND" == "before_cache" ]]; then
    :
elif [[ "$COMMAND" == "after_success" ]]; then
    :
elif [[ "$COMMAND" == "after_failure" ]]; then
    :
elif [[ "$COMMAND" == "before_deploy" ]]; then
    :
elif [[ "$COMMAND" == "after_deploy" ]]; then
    :
elif [[ "$COMMAND" == "after_script" ]]; then
    :
elif [[ -z "$COMMAND" ]]; then
    echo "$NAME requires a command like 'install' or 'script'"
else
    echo "$NAME does not define $COMMAND"
fi
if [[ "$COMMAND" != "script" && -n "$TRAVIS" && "$STATUS" -ne "0" ]]; then
    travis_terminate "$STATUS"
fi
exit "$STATUS"

There is a lot of information in that file that we don’t really need to worry about (that’s the whole point of Nengo Bones, to take care of those details for us). We can see that the overall structure is made up of behaviour defined for different build stages (e.g. “install”, “before_script”, etc.). In most cases all the important action happens in the “install” and “script” stages:

[3]:
display_contents("test.sh", sections=["install", "script"])
if [[ "$COMMAND" == "install" ]]; then
    :
    exe pip install "pytest>=3.6.0"
    exe pip install "pytest-xdist>=1.16.0"
    exe pip install -e ".[tests]"
elif [[ "$COMMAND" == "script" ]]; then
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS

In the “install” stage we install any of the package requirements needed to run the script. In this case we can see we’re installing pytest, pytest-xdist, and the current package (including any optional dependencies defined in the [tests] extra_requires directive).

If we need to add extra packages to this default installation, that can be done with the pip_install configuration option:

[4]:
nengobones_yml = """
ci_scripts:
    - template: test
      pip_install:
          - an-extra-pip-package
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
display_contents("test.sh", sections=["install"])
if [[ "$COMMAND" == "install" ]]; then
    exe pip install "an-extra-pip-package"
    exe pip install "pytest>=3.6.0"
    exe pip install "pytest-xdist>=1.16.0"
    exe pip install -e ".[tests]"

Note that requirements should generally be added to the package requirements defined in setup.py (in which case they would be automatically installed in the pip install -e ".[tests]" step). That way anyone can easily install the necessary packages and run the tests without having to go through the CI scripts. The pip_install option is only necessary if that isn’t feasible for some reason.

The “script” stage is where the main work of the script is done. In the case of the “test” script, this means calling pytest to run the test suite:

[5]:
display_contents("test.sh", sections=["script"])
elif [[ "$COMMAND" == "script" ]]; then
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS

Ignore the $TEST_ARGS part for now. We will cover that when we come to the .travis.yml configuration.

As with the “install” stage, the “script” stage can also be customized if we want to add extra commands, either before or after the main script body:

[6]:
nengobones_yml = """
ci_scripts:
    - template: test
      pre_commands:
          - echo "this command will run at the beginning"
      post_commands:
          - echo "this command will run at the end"
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
display_contents("test.sh", sections=["script"])
elif [[ "$COMMAND" == "script" ]]; then
    exe echo "this command will run at the beginning"
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS
    exe echo "this command will run at the end"

We can also use the same template multiple times, passing different options to generate different output scripts. In this case we need to use the output_name config option to distinguish different rendered scripts:

[7]:
nengobones_yml = """
ci_scripts:
    - template: test
      pre_commands:
          - echo "this is test"
    - template: test
      output_name: test2
      pre_commands:
          - echo "this is test2"
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
print("Contents of test.sh")
print("-------------------")
display_contents("test.sh", sections=["script"])
print("\nContents of test2.sh")
print("--------------------")
display_contents("test2.sh", sections=["script"])
Contents of test.sh
-------------------
elif [[ "$COMMAND" == "script" ]]; then
    exe echo "this is test"
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS

Contents of test2.sh
--------------------
elif [[ "$COMMAND" == "script" ]]; then
    exe echo "this is test2"
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS

Test script options

The “test” script has some configuration options specific to that script. First, we can collect coverage information while tests are running by setting coverage: true:

[8]:
nengobones_yml = """
ci_scripts:
    - template: test
      coverage: true
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
display_contents("test.sh", sections=["install", "script", "after_script"])
if [[ "$COMMAND" == "install" ]]; then
    :
    exe pip install "pytest>=3.6.0"
    exe pip install "pytest-xdist>=1.16.0"
    exe pip install "pytest-cov>=2.6.0"
    exe pip install -e ".[tests]"
elif [[ "$COMMAND" == "script" ]]; then
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 --cov=eg_package --cov-report=term-missing $TEST_ARGS
elif [[ "$COMMAND" == "after_script" ]]; then
    exe eval "bash <(curl -s https://codecov.io/bash)"

Note that the script is now installing extra packages, adding extra arguments to pytest, and uploading the results to Codecov at the end.

Nengo backends also often want to run the core Nengo tests in addition to their own test suite. This can be accomplished by setting nengo_tests: true:

[9]:
nengobones_yml = """
ci_scripts:
    - template: test
      nengo_tests: true
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
display_contents("test.sh", sections=["script"])
elif [[ "$COMMAND" == "script" ]]; then
    # shellcheck disable=SC2086
    exe pytest eg_package -v -n 2 --color=yes --durations 20 $TEST_ARGS
    # shellcheck disable=SC2086
    exe pytest --pyargs nengo -v -n 2 --color=yes --durations 20 $TEST_ARGS

Static script options

The “static” script has some configuration options specific to that script. We can add words to the codespell ignore list by setting the codespell_ignore_words option:

[10]:
nengobones_yml = """
ci_scripts:
    - template: static
      codespell_ignore_words:
          - onne
          - twwo
"""
write_yml(nengobones_yml)
!bones-generate ci-scripts
display_contents("static.sh", sections=["script"])
elif [[ "$COMMAND" == "script" ]]; then
    exe pylint eg_package --rcfile=setup.cfg --jobs=0
    exe flake8 eg_package
    if ls docs/**/*.ipynb &>/dev/null; then
        exe jupyter nbconvert \
            --log-level WARN \
            --to python \
            --TemplateExporter.exclude_input_prompt=True \
            --TemplateExporter.exclude_markdown=True \
            -- docs/**/*.ipynb
        # Remove style issues introduced in the conversion:
        #   s/# $/#/g replaces lines with just '# ' with '#'
        #   /get_ipython()/d removes lines containing 'get_ipython()'
        #   $ d removes a trailing newline
        for nb_file in docs/**/*.ipynb; do
            sed -i \
                -e 's/# $/#/g' \
                -e '/get_ipython()/d' \
                -e '$ d' \
                -- "${nb_file%.ipynb}.py"
        done
    fi
    if [[ "$(python -c 'import sys; print(sys.version_info >= (3, 6))')" == "True" ]]; then
        exe black --check eg_package
    fi
    exe pylint docs --rcfile=setup.cfg --jobs=0 --disable=missing-docstring,trailing-whitespace,wrong-import-position,unnecessary-semicolon
    exe flake8 docs --extend-ignore=E402,E703,W291,W293,W391
    for nb_file in docs/**/*.ipynb; do
        rm "${nb_file%.ipynb}.py"
    done
    exe codespell -q 3 \
        --skip="./build,*/_build,*-checkpoint.ipynb,./.eggs,./.git,*/_vendor" \
        --ignore-words-list="onne,twwo"
    exe shellcheck -e SC2087 .ci/*.sh
    # undo single-branch cloning
    git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
    git fetch origin master
    N_COMMITS=$(git rev-list --count HEAD ^origin/master)
    for ((i=0; i<N_COMMITS; i++)) do
        git log -n 1 --skip $i --pretty=%B \
            | grep -v '^Co-authored-by:' \
            | gitlint -vvv || STATUS=1
    done
    exe python setup.py checkdocs

TravisCI config

As mentioned above, the general idea is that TravisCI will create a number of build jobs, and each job will call one of the scripts we generated. TravisCI is configured through its own .travis.yml file, so what we will be doing is using .nengobones.yml to generate .travis.yml. This is done through the travis_yml section:

[11]:
nengobones_yml = """
travis_yml:
    jobs: []
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml")
# Automatically generated by nengo-bones, do not edit this file directly
language: python
python: 3.6
notifications:
  email:
    on_success: change
    on_failure: change
cache: pip
dist: xenial
env:
  global:
    - SCRIPT="test"
    - TEST_ARGS=""
    - BRANCH_NAME="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
jobs:
  include:
before_install:
  # export travis_terminate for use in scripts, from here:
  # https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/bash/travis_terminate.bash
  - export -f travis_terminate
    _travis_terminate_agent
    _travis_terminate_freebsd
    _travis_terminate_linux
    _travis_terminate_osx
    _travis_terminate_unix
    _travis_terminate_windows
  # upgrade pip
  - pip install pip --upgrade
  # install/run nengo-bones
  - pip install git+https://github.com/nengo/nengo-bones#egg=nengo-bones
  - bones-generate --output-dir .ci ci-scripts
  - if [[ "$TRAVIS_PYTHON_VERSION" < "3.6" ]]; then
        echo "Skipping bones-check because Python $TRAVIS_PYTHON_VERSION < 3.6";
    else
        bones-check --verbose;
    fi
  # display environment info
  - pip freeze
install:
  - .ci/$SCRIPT.sh install
  - pip freeze
before_script:
  - .ci/$SCRIPT.sh before_script
script:
  - .ci/$SCRIPT.sh script
before_cache:
  - .ci/$SCRIPT.sh before_cache
after_success:
  - .ci/$SCRIPT.sh after_success
after_failure:
  - .ci/$SCRIPT.sh after_failure
before_deploy:
  - .ci/$SCRIPT.sh before_deploy
after_deploy:
  - .ci/$SCRIPT.sh after_deploy
after_script:
  - .ci/$SCRIPT.sh after_script

As before, there is a lot of detail here that we don’t need to worry about because Nengo Bones will take care of it for us. The important part to understand is the overall structure.

Global variables

First, we create some global variables:

[12]:
display_contents(".travis.yml", sections=["env"])
env:
  global:
    - SCRIPT="test"
    - TEST_ARGS=""
    - BRANCH_NAME="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"

This shows the default variables. We can add arbitrary variables to this list through the global_vars option:

[13]:
nengobones_yml = """
travis_yml:
    global_vars:
        VAR0: val0
        VAR1: val1
    jobs: []
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["env"])
env:
  global:
    - SCRIPT="test"
    - TEST_ARGS=""
    - BRANCH_NAME="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
    - VAR0="val0"
    - VAR1="val1"

Note that these variables will be set in order, with later variables overwriting earlier ones. So if, for example, we wanted to change the default script, we could do that by setting:

[14]:
nengobones_yml = """
travis_yml:
    global_vars:
        SCRIPT: not-test
    jobs: []
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["env"])
env:
  global:
    - SCRIPT="test"
    - TEST_ARGS=""
    - BRANCH_NAME="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
    - SCRIPT="not-test"

Jobs

The next important part of the .travis.yml is the “jobs” section.

[15]:
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:

It is currently empty because we haven’t defined any jobs in our .nengobones.yml.

This is done via the jobs section of travis_yml, which specifies the builds we want to run during CI. The most important part of each job is specifying which script we want to run. For example, we could create a single job that runs the “test” script:

[16]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"

We can see that this causes the $SCRIPT variable to be set to "test", which will then cause test.sh to be run during the various build steps.

The test_args option sets the $TEST_ARGS environment variable that we saw in the test.sh script above. Recall that this will be passed to the pytest command, like pytest my_project ... $TEST_ARGS. This can be useful for projects that define custom pytest arguments that they might want to set for different jobs, e.g.:

[17]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
          test_args: --do-a-special-test
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
      TEST_ARGS="--do-a-special-test"

We can also set arbitrary environment variables through the env option:

[18]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
          env:
              VAR0: val0
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      VAR0="val0"
      SCRIPT="test"

The apt_install option can be used to apt install any packages required for a job, e.g.:

[19]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
          apt_install:
              - pandoc
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
    addons:
      apt:
        packages:
          - pandoc

Any other options set for a job will be passed directly on to the .travis.yml config for that job. This opens up a wide range of configuration options (see https://docs.travis-ci.com/ for more information).

For example, we can set the python variable to change the Python version being used for that job. So we could create two jobs that run the test suite with different Python versions:

[20]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
          python: 3.5
        - script: test
          python: 3.6
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
    python: 3.5
  -
    env:
      SCRIPT="test"
    python: 3.6

Note that the Python version can also be changed for all jobs by setting the python option at the top level of the travis_yml section:

[21]:
nengobones_yml = """
travis_yml:
    python: 3.5
    jobs:
        - script: test
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=[
    "language", "python", "notifications"])
language: python
python: 3.5
notifications:
  email:
    on_success: change
    on_failure: change

Or we could divide jobs into different stages through the stage option:

[22]:
nengobones_yml = """
travis_yml:
    jobs:
        - script: test
          stage: stage 0
        - script: docs
          stage: stage 1
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
    stage: stage 0
  -
    env:
      SCRIPT="docs"
    addons:
      apt:
        packages:
          - pandoc
    stage: stage 1

Automatic deployment

Nengo Bones can also configure TravisCI to automatically deploy releases to PyPI. This is activated by setting the pypi_user option, which should be the username for the PyPI account that will be used to upload the releases. You will also need to go into the TravisCI settings for your repo and add a secure environment variable named PYPI_TOKEN containing that account’s password. Finally, you will need to make sure you are building the deploy.sh script by adding - template: deploy to your ci_scripts.

[23]:
nengobones_yml = """
ci_scripts:
    - template: test
    - template: deploy

travis_yml:
    jobs:
        - script: test
    pypi_user: test_user
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
  - stage: deploy
    if: branch =~ ^release-candidate-* OR tag =~ ^v[0-9]*
    env: SCRIPT="deploy"
    cache: false
    deploy:
      - provider: pypi
        server: https://test.pypi.org/legacy/
        user: test_user
        password: $PYPI_TOKEN
        distributions: "sdist "
        on:
          all_branches: true
          tags: false
          condition: $TRAVIS_BRANCH =~ ^release-candidate-*
      - provider: pypi
        user: test_user
        password: $PYPI_TOKEN
        distributions: "sdist "
        on:
          all_branches: true
          tags: true
          condition: $TRAVIS_TAG =~ ^v[0-9]*

This will trigger an automatic PyPI release for any tagged commit. You can also do a test release by pushing to a branch named release-candidate-x.y.z (where x.y.z is the version number you want to test).

The only other setting related to the automatic deployment is deploy_dists, which sets the distributions that will be built and uploaded with each release. We can see above that the default is an sdist-only release, but we could, for example, add wheels by modifying deploy_dists:

[24]:
nengobones_yml = """
ci_scripts:
    - template: test
    - template: deploy

travis_yml:
    jobs:
        - script: test
    pypi_user: test_user
    deploy_dists:
        - sdist
        - bdist_wheel
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["jobs"])
jobs:
  include:
  -
    env:
      SCRIPT="test"
  - stage: deploy
    if: branch =~ ^release-candidate-* OR tag =~ ^v[0-9]*
    env: SCRIPT="deploy"
    cache: false
    deploy:
      - provider: pypi
        server: https://test.pypi.org/legacy/
        user: test_user
        password: $PYPI_TOKEN
        distributions: "sdist bdist_wheel "
        on:
          all_branches: true
          tags: false
          condition: $TRAVIS_BRANCH =~ ^release-candidate-*
      - provider: pypi
        user: test_user
        password: $PYPI_TOKEN
        distributions: "sdist bdist_wheel "
        on:
          all_branches: true
          tags: true
          condition: $TRAVIS_TAG =~ ^v[0-9]*

Python environment setup

The next section of the .travis.yml file sets up the Python environment:

[25]:
display_contents(".travis.yml", sections=["before_install"])
before_install:
  # export travis_terminate for use in scripts, from here:
  # https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/bash/travis_terminate.bash
  - export -f travis_terminate
    _travis_terminate_agent
    _travis_terminate_freebsd
    _travis_terminate_linux
    _travis_terminate_osx
    _travis_terminate_unix
    _travis_terminate_windows
  # upgrade pip
  - pip install pip --upgrade
  # install/run nengo-bones
  - pip install git+https://github.com/nengo/nengo-bones#egg=nengo-bones
  - bones-generate --output-dir .ci ci-scripts
  - if [[ "$TRAVIS_PYTHON_VERSION" < "3.6" ]]; then
        echo "Skipping bones-check because Python $TRAVIS_PYTHON_VERSION < 3.6";
    else
        bones-check --verbose;
    fi
  # display environment info
  - pip freeze

Note that we are calling bones-generate ... ci-scripts in this section in order to automatically generate all the scripts defined in the .nengobones.yml ci_scripts discussed above. We also call bones-check, which will give an error if any of the templated files are out of date.

There are no configuration options for this section.

Build steps

Finally we call our templated script in each of the build steps, which will execute the behaviour we defined in those scripts.

[26]:
display_contents(".travis.yml", sections=[
    "install", "after_install", "before_script", "script"
])
print("...")
install:
  - .ci/$SCRIPT.sh install
  - pip freeze
install:
  - .ci/$SCRIPT.sh install
  - pip freeze
before_script:
  - .ci/$SCRIPT.sh before_script
script:
  - .ci/$SCRIPT.sh script
...

There are no configuration options for this section (instead this behaviour is controlled by configuring the ci scripts, as described in the section above).

Slack notifications

The slack_notifications config option can be used to enable Slack notifications on failed builds. This uses a secret string, which can be generated using the Travis CI command line tool

travis encrypt <account>:<token>#<slack-channel>

Where account and token can be obtained from Slack’s Travis CI integration settings, and slack-channel is where we want messages to be delivered. This will output a string that looks like

secure: "<secret-key>"

Copy that secret key and set it as the value of slack_notifications:

[27]:
nengobones_yml = """
travis_yml:
    slack_notifications: <secret-key>
    jobs: []
"""
write_yml(nengobones_yml)
!bones-generate travis-yml
display_contents(".travis.yml", sections=["notifications"])
notifications:
  email:
    on_success: change
    on_failure: change
  slack:
    if: branch = master
    on_pull_requests: false
    on_success: change
    on_failure: always
    rooms:
      - secure: "<secret-key>"

Codecov config

Nengo Bones will automatically upload coverage reports to Codecov if the coverage: true option is set on the test script config. Codecov reads the .codecov.yml configuration file, which Nengo Bones also has a template for and can be controlled through the codecov_yml option.

[28]:
nengobones_yml = """
codecov_yml: {}
"""
write_yml(nengobones_yml)
!bones-generate codecov-yml
display_contents(".codecov.yml")
# Automatically generated by nengo-bones, do not edit this file directly
codecov:
  ci:
    - "!ci.appveyor.com"
  notify:
    require_ci_to_pass: no
coverage:
  status:
    project:
      default:
        enabled: yes
        target: auto
    patch:
      default:
        enabled: yes
        target: 100%
    changes: no

The first thing to be noted is the !ci.appveyor.com line. This tells Codecov not to wait for a coverage report from appveyor (generally we assume that all the coverage is being checked on TravisCI). This can be disabled by setting skip_appveyor: false:

[29]:
nengobones_yml = """
codecov_yml:
    skip_appveyor: false
"""
write_yml(nengobones_yml)
!bones-generate codecov-yml
display_contents(".codecov.yml", sections=["codecov"])
codecov:
  notify:
    require_ci_to_pass: no

The other configuration options have to do with the target coverage at either the project or patch level. These determine whether the codecov PR status checks will be marked as passing or failing. The “project” coverage is the total lines of code covered by tests for the whole project. The default value is “auto”, meaning that the status check will pass if the total coverage is >= the coverage in the base branch of the PR. The “patch” coverage only looks at the lines of code modified in the PR. The default value is “100%”, meaning that every modified line of code needs to be covered by a test for the check to pass.

These can be modified by the abs_target and diff_target config options, respectively:

[30]:
nengobones_yml = """
codecov_yml:
    abs_target: 80%
    diff_target: 90%
"""
write_yml(nengobones_yml)
!bones-generate codecov-yml
display_contents(".codecov.yml", sections=["coverage"])
coverage:
  status:
    project:
      default:
        enabled: yes
        target: 80%
    patch:
      default:
        enabled: yes
        target: 90%
    changes: no

LICENSE.rst config

A license file can be automatically generated by Nengo Bones. Currently the Nengo License and MIT License are supported as base templates (specified by the type tag).

[31]:
nengobones_yml = """
license_rst:
    type: nengo
"""
write_yml(nengobones_yml)
!bones-generate license-rst
display_contents("LICENSE.rst")
.. Automatically generated by nengo-bones, do not edit this file directly

***********************
Example Project license
***********************

Copyright (c) 2020-2020 Applied Brain Research

Example Project is made available under a proprietary license
that permits using, copying, sharing, and making derivative works from
Example Project and its source code for any non-commercial purpose,
as long as the above copyright notice and this permission notice
are included in all copies or substantial portions of the software.

If you would like to use Example Project commercially,
licenses can be purchased from Applied Brain Research.
Please contact [email protected] for more information.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Note that this template uses several of the general options described above (project_name, author, author_email, copyright_start, copyright_end).

[32]:
nengobones_yml = """
author: A Different Author
copyright_start: 1900
license_rst:
    type: mit
"""
write_yml(nengobones_yml)
!bones-generate license-rst
display_contents("LICENSE.rst")
.. Automatically generated by nengo-bones, do not edit this file directly

***********************
Example Project license
***********************

MIT License

Copyright (c) 1900-2020 A Different Author

Permission is hereby granted, free of charge,
to any person obtaining a copy of this software
and associated documentation files (the "Software"),
to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

CONTRIBUTING.rst config

The CONTRIBUTING.rst file instructs potential contributors on how to contribute to the project. It has no configuration options, although it makes use of some of the general options (project_name, repo_name).

[33]:
nengobones_yml = """
contributing_rst: {}
"""
write_yml(nengobones_yml)

!bones-generate contributing-rst
display_contents("CONTRIBUTING.rst")
.. Automatically generated by nengo-bones, do not edit this file directly

*******************************
Contributing to Example Project
*******************************

Issues and pull requests are always welcome!
We appreciate help from the community to make Example Project better.

Filing issues
=============

If you find a bug in Example Project,
or think that a certain feature is missing,
please consider
`filing an issue <https://github.com/eg-org/eg-package/issues>`_!
Please search the currently open issues first
to see if your bug or feature request already exists.
If so, feel free to add a comment to the issue
so that we know that multiple people are affected.

Making pull requests
====================

If you want to fix a bug or add a feature to Example Project,
we welcome pull requests.
Ensure that you fill out all sections of the pull request template,
deleting the comments as you go.
We check most aspects of code style automatically.
Please refer to our
`code style guide <https://www.nengo.ai/nengo-bones/style.html>`_
for things that we check manually.

Contributor agreement
=====================

We require that all contributions be covered under
our contributor assignment agreement. Please see
`the agreement <https://www.nengo.ai/caa/>`_
for instructions on how to sign.

More details
============

For more details on how to contribute to Nengo,
please see the `developer guide <https://www.nengo.ai/contributing/>`_.

CONTRIBUTORS.rst config

The CONTRIBUTORS.rst file links to the contributor list for the project. It always provides the Github contributors link, which lists all people who have committed to the project.

[34]:
nengobones_yml = """
contributors_rst: {}
"""
write_yml(nengobones_yml)

!bones-generate contributors-rst
display_contents("CONTRIBUTORS.rst")
.. Automatically generated by nengo-bones, do not edit this file directly

****************************
Example Project contributors
****************************

See https://github.com/eg-org/eg-package/graphs/contributors
for a list of the people who have committed to Example Project.
Thank you for your contributions!

The nengo_list flag includes the full list of Nengo ecosystem contributors. It defaults to True if the license type is the “nengo” license, and False otherwise.

[35]:
nengobones_yml = """
contributors_rst:
    nengo_list: True
"""
write_yml(nengobones_yml)

!bones-generate contributors-rst
display_contents("CONTRIBUTORS.rst")
.. Automatically generated by nengo-bones, do not edit this file directly

****************************
Example Project contributors
****************************

See https://github.com/eg-org/eg-package/graphs/contributors
for a list of the people who have committed to Example Project.
Thank you for your contributions!

For the full list of the many contributors to the Nengo ecosystem,
see https://www.nengo.ai/people/.

MANIFEST.in config

The MANIFEST.in template defines some standard rules for the project manifest:

[36]:
nengobones_yml = """
manifest_in: {}
"""
write_yml(nengobones_yml)

!bones-generate manifest-in
display_contents("MANIFEST.in")
# Automatically generated by nengo-bones, do not edit this file directly

global-include *.py
global-include *.sh
global-include *.template
include *.rst

# Include files for CI and recreating the source dist
include *.yml
include *.yaml
include *.toml
include MANIFEST.in
include .gitlint
include .pylintrc

# Directories to include
graft docs

# Subdirectories to exclude, if they exist
prune docs/_build
prune dist
prune .git
prune .github
prune .tox
prune .eggs
prune .ci

# Exclude auto-generated files
recursive-exclude docs *.py

# Patterns to exclude from any directory
global-exclude *.ipynb_checkpoints*
global-exclude *-checkpoint.ipynb

# Exclude all bytecode
global-exclude *.pyc *.pyo *.pyd

Arbitrary additions can be made to this list by setting configuration options with the appropriate MANIFEST rule heading:

[37]:
nengobones_yml = """
manifest_in:
    global-include:
        - global_include_pattern0
        - global_include_pattern1
    graft:
        - graft_pattern0
"""
write_yml(nengobones_yml)

!bones-generate manifest-in
display_contents("MANIFEST.in")
# Automatically generated by nengo-bones, do not edit this file directly

global-include *.py
global-include *.sh
global-include *.template
include *.rst

# Include files for CI and recreating the source dist
include *.yml
include *.yaml
include *.toml
include MANIFEST.in
include .gitlint
include .pylintrc

# Directories to include
graft docs

# Subdirectories to exclude, if they exist
prune docs/_build
prune dist
prune .git
prune .github
prune .tox
prune .eggs
prune .ci

# Exclude auto-generated files
recursive-exclude docs *.py

# Patterns to exclude from any directory
global-exclude *.ipynb_checkpoints*
global-exclude *-checkpoint.ipynb

# Exclude all bytecode
global-exclude *.pyc *.pyo *.pyd

# Repo-specific files
global-include global_include_pattern0
global-include global_include_pattern1
graft graft_pattern0

setup.cfg config

The setup.cfg file contains configuration options for a number of different tools. In addition to the specific options defined below, the setup.cfg template makes use of the general pkg_name option to set various file/path names.

[38]:
nengobones_yml = """
setup_cfg: {}
"""
write_yml(nengobones_yml)

!bones-generate setup-cfg
display_contents("setup.cfg")
# Automatically generated by nengo-bones, do not edit this file directly

[build_sphinx]
source-dir = docs
build-dir = docs/_build
all_files = 1

[coverage:run]
source = eg_package

[coverage:report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    # place ``# pragma: no cover`` at the end of a line to ignore it
    pragma: no cover

    # Don't complain if tests don't hit defensive assertion code:
    raise NotImplementedError

    # `pass` is just a placeholder, fine if it's not covered
    ^[ \t]*pass$


# Patterns for files to exclude from reporting
omit =
    */tests/test*

[flake8]
exclude =
    __init__.py
ignore =
    E123
    E133
    E203
    E226
    E241
    E242
    E501
    E731
    F401
    W503
max-complexity = 10
max-line-length = 88

[tool:pytest]
norecursedirs =
    .*
    *.egg
    build
    dist
    docs
xfail_strict = False

[pylint]
# note: pylint doesn't look in setup.cfg by default, need to call it with
# `pylint ... --rcfile=setup.cfg`
disable =
    arguments-differ,
    assignment-from-no-return,
    attribute-defined-outside-init,
    bad-continuation,
    blacklisted-name,
    comparison-with-callable,
    duplicate-code,
    fixme,
    import-error,
    invalid-name,
    invalid-sequence-index,
    len-as-condition,
    literal-comparison,
    no-else-raise,
    no-else-return,
    no-member,
    no-name-in-module,
    no-self-use,
    not-an-iterable,
    not-context-manager,
    protected-access,
    redefined-builtin,
    stop-iteration-return,
    too-few-public-methods,
    too-many-arguments,
    too-many-branches,
    too-many-instance-attributes,
    too-many-lines,
    too-many-locals,
    too-many-return-statements,
    too-many-statements,
    unexpected-keyword-arg,
    unidiomatic-typecheck,
    unsubscriptable-object,
    unsupported-assignment-operation,
    unused-argument,
known-third-party =
    matplotlib,
    nengo,
    numpy,
    pytest,
max-line-length = 88
valid-metaclass-classmethod-first-arg = metacls
reports = no
score = no

Each of the tools configured by setup.cfg has its own subheading in the .nengobones.yml setup_cfg section.

coverage has two options: - exclude_lines: a list of regex expressions that will be ignored in code coverage reports - omit_files: a list of filename patterns that will be ignored

Note that some defaults are already specified in both cases, and any options passed are in addition to those defaults.

[39]:
nengobones_yml = """
setup_cfg:
    coverage:
        exclude_lines:
            # Ignore all loops
            - for *
            - while *
        omit_files:
            # Ignore all files in subdirectory
            - subdir/*
"""
write_yml(nengobones_yml)

!bones-generate setup-cfg
display_contents("setup.cfg", sections=["[coverage:run]", "[coverage:report]"])
[coverage:run]
source = eg_package

[coverage:report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    # place ``# pragma: no cover`` at the end of a line to ignore it
    pragma: no cover

    # Don't complain if tests don't hit defensive assertion code:
    raise NotImplementedError

    # `pass` is just a placeholder, fine if it's not covered
    ^[ \t]*pass$

    for *
    while *

# Patterns for files to exclude from reporting
omit =
    */tests/test*
    subdir/*

The flake8 section has two options:

[40]:
nengobones_yml = """
setup_cfg:
    flake8:
        exclude:
            - subdir/*
        ignore:
            - E101
            - E111
"""
write_yml(nengobones_yml)

!bones-generate setup-cfg
display_contents("setup.cfg", sections=["[flake8]"])
[flake8]
exclude =
    __init__.py
    subdir/*
ignore =
    E123
    E133
    E203
    E226
    E241
    E242
    E501
    E731
    F401
    W503
    E101
    E111
max-complexity = 10
max-line-length = 88

The pytest section has the following general options:

  • addopts: A list of command line options to pass to any pytest invocation (defaults to -p nengo.tests.options, which will add the pytest command line options from Nengo core).

  • xfail_strict: If True, xpass will be treated as a failure (defaults to False).

  • norecursedirs: A list of directory name patterns that should not be searched for test files.

  • markers: Descriptions for any custom markers that can be applied to test functions.

  • filterwarnings: Controls how warnings will be handled (see https://docs.python.org/3/library/warnings.html#warning-filter for a description of how to write warning filters).

And the following options for Pytest plugins.

pytest-allclose:

  • allclose_tolerances: A list of test name patterns and associated tolerance values.

pytest_nengo:

  • nengo_neurons: Specifies which neuron types to test.

  • nengo_simulator: Specifies the Nengo backend to test.

  • nengo_simloader: Specifies a function that returns the Nengo backend to test.

  • nengo_test_unsupported: Marks which core tests are not expected to pass.

pytest-plt:

  • plt_dirname: A directory name to store plots.

  • plt_filename_drop: A list of regular expressions to drop parts of plot filenames.

pytest-rng:

  • rng_salt: A salt value to modify seeds.

[41]:
nengobones_yml = r"""
setup_cfg:
    pytest:
        addopts:
            - -p nengo.tests.options
            - --colors=yes
        xfail_strict: true
        norecursedirs:
            - subdir0
            - subdir1
        markers:
            marker0: description of marker0
            marker1: description of marker1
        filterwarnings:
            - ignore::UserWarning
            - error::ResourceWarning
        nengo_test_unsupported:
            test_ensemble.py::test_encoders*:
                This backend does not support encoders
        allclose_tolerances:
            - "test_ensemble.py::test_encoders* atol=0.01 rtol=0.05  # comment"
        plt_dirname: nengo.plots
        plt_filename_drop:
            - "^nengo\\."
        rng_salt: v1.0.0
"""
write_yml(nengobones_yml)

!bones-generate setup-cfg
display_contents("setup.cfg", sections=["[tool:pytest]"])
[tool:pytest]
addopts = -p nengo.tests.options --colors=yes
norecursedirs =
    .*
    *.egg
    build
    dist
    docs
    subdir0
    subdir1
markers =
    marker0: description of marker0
    marker1: description of marker1
xfail_strict = True
filterwarnings =
    ignore::UserWarning
    error::ResourceWarning
nengo_test_unsupported =
    test_ensemble.py::test_encoders*
        "This backend does not support encoders"
allclose_tolerances =
    test_ensemble.py::test_encoders* atol=0.01 rtol=0.05  # comment
plt_dirname = nengo.plots
plt_filename_drop =
    ^nengo\.
rng_salt = v1.0.0

The pylint section has three options:

  • ignore: A list of filename patterns to ignore.

  • disable: A list of pylint rules to ignore (see https://github.com/janjur/readable-pylint-messages). Note that a number of rules are already disabled by default, and any rules specified here will be in addition to those.

  • known-third-party: A list of third party modules (sometimes pylint will fail to detect that a module is third-party, and then complain about the import order).

[42]:
nengobones_yml = """
setup_cfg:
    pylint:
        ignore:
            - __init__.py
        disable:
            - empty-docstring
            - unneeded-not
        known_third_party:
            - scipy
"""
write_yml(nengobones_yml)

!bones-generate setup-cfg
display_contents("setup.cfg", sections=["[pylint]"])
[pylint]
# note: pylint doesn't look in setup.cfg by default, need to call it with
# `pylint ... --rcfile=setup.cfg`
ignore =
    __init__.py,
disable =
    arguments-differ,
    assignment-from-no-return,
    attribute-defined-outside-init,
    bad-continuation,
    blacklisted-name,
    comparison-with-callable,
    duplicate-code,
    fixme,
    import-error,
    invalid-name,
    invalid-sequence-index,
    len-as-condition,
    literal-comparison,
    no-else-raise,
    no-else-return,
    no-member,
    no-name-in-module,
    no-self-use,
    not-an-iterable,
    not-context-manager,
    protected-access,
    redefined-builtin,
    stop-iteration-return,
    too-few-public-methods,
    too-many-arguments,
    too-many-branches,
    too-many-instance-attributes,
    too-many-lines,
    too-many-locals,
    too-many-return-statements,
    too-many-statements,
    unexpected-keyword-arg,
    unidiomatic-typecheck,
    unsubscriptable-object,
    unsupported-assignment-operation,
    unused-argument,
    empty-docstring,
    unneeded-not,
known-third-party =
    matplotlib,
    nengo,
    numpy,
    pytest,
    scipy,
max-line-length = 88
valid-metaclass-classmethod-first-arg = metacls
reports = no
score = no

setup.py config

The setup.py template has a number of configuration options, and also makes use of some of the general config settings (pkg_name, author, author_email, description).

[43]:
nengobones_yml = """
setup_py: {}
"""
write_yml(nengobones_yml)

!bones-generate setup-py
display_contents("setup.py")
#!/usr/bin/env python

# Automatically generated by nengo-bones, do not edit this file directly

import io
import os
import runpy

try:
    from setuptools import find_packages, setup
except ImportError:
    raise ImportError(
        "'setuptools' is required but not installed. To install it, "
        "follow the instructions at "
        "https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py"
    )


def read(*filenames, **kwargs):
    encoding = kwargs.get("encoding", "utf-8")
    sep = kwargs.get("sep", "\n")
    buf = []
    for filename in filenames:
        with io.open(filename, encoding=encoding) as f:
            buf.append(f.read())
    return sep.join(buf)


root = os.path.dirname(os.path.realpath(__file__))
version = runpy.run_path(os.path.join(root, "eg_package", "version.py"))["version"]

install_req = []
docs_req = []
optional_req = []
tests_req = []

setup(
    name="eg-package",
    version=version,
    author="Applied Brain Research",
    author_email="[email protected]",
    packages=find_packages(),
    url="https://www.nengo.ai/eg-package",
    include_package_data=False,
    license="Free for non-commercial use",
    description="",
    long_description=read("README.rst", "CHANGES.rst"),
    zip_safe=False,
    install_requires=install_req,
    extras_require={
        "all": docs_req + optional_req + tests_req,
        "docs": docs_req,
        "optional": optional_req,
        "tests": tests_req,
    },
    python_requires=">=3.5",
)

Package dependencies are defined in the install_req (the main dependencies required to use the package), optional_req (dependencies that aren’t necessary but activate extra functionality), docs_req (requirements for building the documentation), and tests_req (requirements for running the test suite) fields.

[44]:
nengobones_yml = """
setup_py:
    install_req:
        - package0
        - package1
    optional_req:
        - package2
        - package3
    docs_req:
        - package4
        - package5
    tests_req:
        - package6
        - package7
"""
write_yml(nengobones_yml)

!bones-generate setup-py
display_contents("setup.py", sections=[
    "install_req", "optional_req", "docs_req", "tests_req"])
install_req = [
    "package0",
    "package1",
optional_req = [
    "package2",
    "package3",
docs_req = [
    "package4",
    "package5",
tests_req = [
    "package6",
    "package7",

There are a number of options that will be passed to the setup function:

  • url: A link to the project’s homepage (defaults to nengo.ai/my-package).

  • python_requires: Python version requirement for this package (defaults to >=3.5).

  • include_package_data: Whether or not to include files listed in MANIFEST.in in the installation (defaults to False).

  • package_data: Mapping of extra data files to include in installation (not specified by MANIFEST.in).

  • classifiers: List of Trove classifiers.

  • entry_points: Dictionary of entry points to make available.

See the distutils and setuptools documentation for more details on these options.

[45]:
nengobones_yml = """
setup_py:
    url: https://github.com/org/my-package
    python_requires: ">=3.4"
    include_package_data: true
    package_data:
        pkg:
            - file0
            - file1
    classifiers:
        - "Development Status :: 3 - Alpha"
        - "Framework :: Nengo"
    entry_points:
        nengo.backends:
            - my_backend = my_backend:Simulator
        console_scripts:
            - my-script = my_project.file:function
"""
write_yml(nengobones_yml)

!bones-generate setup-py
display_contents("setup.py", sections=["setup"])
setup(
    name="eg-package",
    version=version,
    author="Applied Brain Research",
    author_email="[email protected]",
    packages=find_packages(),
    url="https://github.com/org/my-package",
    include_package_data=True,
    license="Free for non-commercial use",
    description="",
    long_description=read("README.rst", "CHANGES.rst"),
    zip_safe=False,
    install_requires=install_req,
    extras_require={
        "all": docs_req + optional_req + tests_req,
        "docs": docs_req,
        "optional": optional_req,
        "tests": tests_req,
    },
    python_requires=">=3.4",
    package_data={"pkg": ["file0", "file1",],},
    classifiers=["Development Status :: 3 - Alpha", "Framework :: Nengo",],
    entry_points={
        "nengo.backends": ["my_backend = my_backend:Simulator",],
        "console_scripts": ["my-script = my_project.file:function",],
    },

docs/conf.py config

The docs/conf.py file contains settings controlling Sphinx documentation. It makes use of several general configuration option (pkg_name, project_name, author, copyright_start, copyright_end).

[46]:
nengobones_yml = """
docs_conf_py: {}
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py")
# -*- coding: utf-8 -*-
#
# Automatically generated by nengo-bones, do not edit this file directly

import os

import eg_package

extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.doctest",
    "sphinx.ext.githubpages",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.todo",
    "sphinx.ext.viewcode",
    "nbsphinx",
    "nengo_sphinx_theme",
    "nengo_sphinx_theme.ext.backoff",
    "numpydoc",
]

# -- sphinx.ext.autodoc
autoclass_content = "both"  # class and __init__ docstrings are concatenated
autodoc_default_options = {"members": None}
autodoc_member_order = "bysource"  # default is alphabetical

# -- sphinx.ext.doctest
doctest_global_setup = """
import eg_package
"""

# -- sphinx.ext.intersphinx
intersphinx_mapping = {
    "nengo": ("https://www.nengo.ai/nengo/", None),
    "numpy": ("https://docs.scipy.org/doc/numpy", None),
    "python": ("https://docs.python.org/3", None),
}

# -- sphinx.ext.todo
todo_include_todos = True

# -- numpydoc config
numpydoc_show_class_members = False

# -- nbsphinx
nbsphinx_timeout = -1

# -- sphinx
nitpicky = True
exclude_patterns = [
    "_build",
    "**/.ipynb_checkpoints",
]
linkcheck_timeout = 30
source_suffix = ".rst"
source_encoding = "utf-8"
master_doc = "index"
linkcheck_ignore = [r"http://localhost:\d+"]
linkcheck_anchors = True
default_role = "py:obj"
pygments_style = "sphinx"
user_agent = "eg_package"

project = "Example Project"
authors = "Applied Brain Research"
copyright = "2020-2020 Applied Brain Research"
version = ".".join(eg_package.__version__.split(".")[:2])  # Short X.Y version
release = eg_package.__version__  # Full version, with tags

# -- HTML output
templates_path = ["_templates"]
html_static_path = ["_static"]
html_theme = "nengo_sphinx_theme"
html_title = "Example Project {0} docs".format(release)
htmlhelp_basename = "Example Project"
html_last_updated_fmt = ""  # Default output format (suppressed)
html_show_sphinx = False
html_favicon = os.path.join("_static", "favicon.ico")
html_theme_options = {
    "nengo_logo": "general-full-light.svg",
    "nengo_logo_color": "#a8acaf",
}

Extra Sphinx extensions can be added by setting the extensions config option:

[47]:
nengobones_yml = """
docs_conf_py:
    extensions:
        - a_sphinx_extension
        - another_sphinx_extension
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["extensions"])
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.doctest",
    "sphinx.ext.githubpages",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.todo",
    "sphinx.ext.viewcode",
    "nbsphinx",
    "nengo_sphinx_theme",
    "nengo_sphinx_theme.ext.backoff",
    "numpydoc",
    "a_sphinx_extension",
    "another_sphinx_extension",

Extra intersphinx mappings (for linking to documentation in other projects) can be added by setting the intersphinx_mapping config option, which is a mapping from package names to documentation URLs (the linked documentations must also be generated by Sphinx).

[48]:
nengobones_yml = """
docs_conf_py:
    intersphinx_mapping:
        scipy: https://docs.scipy.org/doc/scipy/reference
        sklearn: https://scikit-learn.org/dev
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["intersphinx_mapping"])
intersphinx_mapping = {
    "nengo": ("https://www.nengo.ai/nengo/", None),
    "numpy": ("https://docs.scipy.org/doc/numpy", None),
    "python": ("https://docs.python.org/3", None),
    "scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
    "sklearn": ("https://scikit-learn.org/dev", None),

The exclude_patterns config option is a list of file/directory name patterns that should not be included in the documentation rendering.

[49]:
nengobones_yml = """
docs_conf_py:
    exclude_patterns:
        - a-file.py
        - subdir
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["exclude_patterns"])
exclude_patterns = [
    "_build",
    "**/.ipynb_checkpoints",
    "a-file.py",
    "subdir",

The nengo_logo config option contains the name of the logo image that will be rendered in the sidebar. The image filename refers to the nengo/design assets. The default is general-full-light.svg, which refers to this logo.

[50]:
nengobones_yml = """
docs_conf_py:
    nengo_logo: nengo-dl-full-light.svg
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["html_theme_options"])
html_theme_options = {
    "nengo_logo": "nengo-dl-full-light.svg",
    "nengo_logo_color": "#a8acaf",

The nengo_logo_color config option contains the primary color associated with the logo. This information should also be present in the nengo/design repository. The default is #a8acaf, which is a neutral gray appropriate for projects without a primary color.

Note that because # prefixes comments in YAML, you must enclose hex color entries in single or double quotes.

[51]:
nengobones_yml = """
docs_conf_py:
    nengo_logo_color: '#abcdef'
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["html_theme_options"])
html_theme_options = {
    "nengo_logo": "general-full-light.svg",
    "nengo_logo_color": "#abcdef",

The analytics_id config option contains a Google Analytics tracking ID that will enable Google Analytics tracking. It should look like UA-000000-2. Projects with documentation hosted at https://www.nengo.ai should use the key UA-41658423-2.

[52]:
nengobones_yml = """
docs_conf_py:
    analytics_id: UA-41658423-2
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["html_theme_options"])
html_theme_options = {
    "nengo_logo": "general-full-light.svg",
    "nengo_logo_color": "#a8acaf",
    "analytics_id": "UA-41658423-2",

The doctest_setup config option contains a list of Python statements that should be executed before the code in .. testcode:: blocks, which are run with the sphinx.ext.doctest extension.

[53]:
nengobones_yml = """
docs_conf_py:
    doctest_setup:
        - import nengo
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py")
# -*- coding: utf-8 -*-
#
# Automatically generated by nengo-bones, do not edit this file directly

import os

import eg_package

extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.doctest",
    "sphinx.ext.githubpages",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.todo",
    "sphinx.ext.viewcode",
    "nbsphinx",
    "nengo_sphinx_theme",
    "nengo_sphinx_theme.ext.backoff",
    "numpydoc",
]

# -- sphinx.ext.autodoc
autoclass_content = "both"  # class and __init__ docstrings are concatenated
autodoc_default_options = {"members": None}
autodoc_member_order = "bysource"  # default is alphabetical

# -- sphinx.ext.doctest
doctest_global_setup = """
import eg_package
import nengo
"""

# -- sphinx.ext.intersphinx
intersphinx_mapping = {
    "nengo": ("https://www.nengo.ai/nengo/", None),
    "numpy": ("https://docs.scipy.org/doc/numpy", None),
    "python": ("https://docs.python.org/3", None),
}

# -- sphinx.ext.todo
todo_include_todos = True

# -- numpydoc config
numpydoc_show_class_members = False

# -- nbsphinx
nbsphinx_timeout = -1

# -- sphinx
nitpicky = True
exclude_patterns = [
    "_build",
    "**/.ipynb_checkpoints",
]
linkcheck_timeout = 30
source_suffix = ".rst"
source_encoding = "utf-8"
master_doc = "index"
linkcheck_ignore = [r"http://localhost:\d+"]
linkcheck_anchors = True
default_role = "py:obj"
pygments_style = "sphinx"
user_agent = "eg_package"

project = "Example Project"
authors = "Applied Brain Research"
copyright = "2020-2020 Applied Brain Research"
version = ".".join(eg_package.__version__.split(".")[:2])  # Short X.Y version
release = eg_package.__version__  # Full version, with tags

# -- HTML output
templates_path = ["_templates"]
html_static_path = ["_static"]
html_theme = "nengo_sphinx_theme"
html_title = "Example Project {0} docs".format(release)
htmlhelp_basename = "Example Project"
html_last_updated_fmt = ""  # Default output format (suppressed)
html_show_sphinx = False
html_favicon = os.path.join("_static", "favicon.ico")
html_theme_options = {
    "nengo_logo": "general-full-light.svg",
    "nengo_logo_color": "#a8acaf",
}

The html_redirects config option contains a mapping from original locations to new locations for generated HTML files that have been moved.

[54]:
nengobones_yml = """
docs_conf_py:
    html_redirects:
        old_file.html: new-file.html
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["extensions", "html_redirects"])
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.doctest",
    "sphinx.ext.githubpages",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.todo",
    "sphinx.ext.viewcode",
    "nbsphinx",
    "nengo_sphinx_theme",
    "nengo_sphinx_theme.ext.backoff",
    "nengo_sphinx_theme.ext.redirects",
    "numpydoc",
html_redirects = [
    ("old_file.html", "new-file.html"),

The autoautosummary_change_modules config option allows the module of classes/functions to be changed in autoautosummary or automodule directives. For example, this can be used so that nengo.ensemble.Ensemble shows up as nengo.Ensemble in the documentation. Note that this depends on the nengo_sphinx_theme.ext.autoautosummary extension.

[55]:
nengobones_yml = """
docs_conf_py:
    autoautosummary_change_modules:
        nengo:
            - nengo.ensemble.Ensemble
            - nengo.connection.Connection
        nengo.another_module:
            - nengo.some.function
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=["autoautosummary_change_modules"])
autoautosummary_change_modules = {
    "nengo": ["nengo.ensemble.Ensemble", "nengo.connection.Connection",],
    "nengo.another_module": ["nengo.some.function",],

Finally, the sphinx_options config option can be used to set arbitrary options of the form var = val in the conf.py file.

[56]:
nengobones_yml = """
docs_conf_py:
    sphinx_options:
        an_option: "'a string value'"
        another_option: ["a", "list", "value"]
"""
write_yml(nengobones_yml)

!bones-generate docs-conf-py
display_contents("docs/conf.py", sections=[
    "default_role", "pygments_style", "an_option", "another_option"])
default_role = "py:obj"
pygments_style = "sphinx"
an_option = "a string value"
another_option = ["a", "list", "value"]

.pre-commit-config.yaml and pyproject.toml configs

The .pre-commit-config.yaml and pyproject.toml files make it easy to install black as a pre-commit hook for your repository and run it on the correct files.

The only configuration available for these files is an exclude option that accepts a list of patterns to exclude. Each entry is a regular expression. The list should be the same for both files.

When you add these files to your .nengobones.yml file, you should also add something to your documentation to indicate that users should configure their editor to run Black automatically, and to install pre-commit and run pre-commit install inside the repository.

[57]:
nengobones_yml = """
pre_commit_config_yaml:
    exclude:
        - project/ignore_me.py
pyproject_toml:
    exclude:
        - project/ignore_me.py
"""
write_yml(nengobones_yml)

!bones-generate pre-commit-config-yaml
display_contents(".pre-commit-config.yaml")
# Automatically generated by nengo-bones, do not edit this file directly

repos:
-   repo: https://github.com/psf/black
    rev: 19.10b0
    hooks:
    - id: black
      exclude: |
          (?x)(
              project/ignore_me.py
          )
[58]:
!bones-generate pyproject-toml
display_contents("pyproject.toml")
# Automatically generated by nengo-bones, do not edit this file directly

[build-system]
requires = ["setuptools", "wheel"]

[tool.black]
target-version = ['py35', 'py36', 'py37']
exclude = '''
(
    project/ignore_me.py
)
'''
[59]:
# clean up the temporary files generated by this notebook

files = list(nengo_bones.all_files) + [
    ".nengobones.yml", "test.sh", "test2.sh", "static.sh"]

for file in files:
    if os.path.exists(file):
        os.remove(file)