Skip to content

Commit

Permalink
Merge pull request #25 from project-rig/config-setup-script
Browse files Browse the repository at this point in the history
Add nengo_spinnaker_setup command.
  • Loading branch information
mundya committed Jun 16, 2015
2 parents 0de3b8e + 7c09c0b commit 01179c2
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 62 deletions.
83 changes: 22 additions & 61 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,69 +17,11 @@ Quick Start

Install using ``pip``::

pip install nengo_spinnaker
$ pip install nengo_spinnaker

Settings File
-------------

To use SpiNNaker with Nengo you must create a ``nengo_spinnaker.conf`` file in
either the directory you will be running your code from or, more usefully, a
centralised location. The centralised location varies based on your operating
system:

- Windows: ``%userprofile%\.nengo\nengo_spinnaker.conf``
- Other: ``~/.config/nengo/nengo_spinnaker.conf``
Configure ``nengo_spinnaker`` to use your local SpiNNaker system::

This file exists to inform ``nengo_spinnaker`` of the nature of the SpiNNaker
machine you wish to simulate with and how to communicate with it. This file may
look like::

### SpiNNaker system configuration
#
# Settings for the SpiNNaker machine which will be used to simulate Nengo
# models.

[spinnaker_machine]
hostname: <host name of the machine here>
width: <width of the machine here>
height: <height of the machine here>

# Required parameters are:
# - hostname: (string) either the hostname or the IP address of the board
# containing chip (0, 0).
# - width: (int) width of the machine (0 <= width < 256)
# - height: (int) height of the machine (0 <= height < 256)
#
# Optional parameters are:
# - "hardware_version: (int) Version number of the SpiNNaker boards
# used in the system (e.g. SpiNN-5 boards would be 5). At the
# time of writing this value is ignored and can be safely set to
# the default value of 0.
# - "led_config": (int) Defines LED pin numbers for the SpiNNaker boards
# used in the system. The four least significant bits (3:0) give
# the number of LEDs. The next four bits give the pin number of the
# first LED, the next four the pin number of the second LED, and so
# forth. At the time of writing, all SpiNNaker board versions have
# their first LED attached to pin 0 and thus the default value of
# 0x00000001 is safe.
#
# For a Spin3 board connected to 192.168.240.253 this section would look
# like:
#
# hostname: 192.168.240.253
# width: 2
# height: 2
# hardware_version: 3
# led_config: 0x00000502
#
# For a Spin5 board connected to 192.168.1.1 this section would look
# like:
#
# hostname: 192.168.1.1
# width: 8
# height: 8
# hardware_version: 5
# led_config: 0x00000001
$ nengo_spinnaker_setup


Using ``nengo_spinnaker``
Expand Down Expand Up @@ -123,6 +65,25 @@ For example::
nengo_spinnaker.add_spinnaker_params(model.config)
model.config[signal].function_of_time = True

Settings File
-------------

In order to know which SpiNNaker system to use, ``nengo_spinnaker`` uses a
config file called ``nengo_spinnaker.conf`` file in either the directory you
will be running your code from or, more usefully, a centralised location. The
centralised location varies based on your operating system:

- Windows: ``%userprofile%\.nengo\nengo_spinnaker.conf``
- Other: ``~/.config/nengo/nengo_spinnaker.conf``

A utility called ``nengo_spinnaker_setup`` installed with ``nengo_spinnaker``
can be used to create this file. By default, the config file is created
centrally but adding the ``--project`` option will create a config file in the
current directory which applies only .

An annotated `example config file <./nengo_spinnaker.conf.example>`_ is provided
for users who wish to create their config file by hand.


Developers
==========
Expand Down
47 changes: 47 additions & 0 deletions nengo_spinnaker.conf.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
### SpiNNaker system configuration
#
# Settings for the SpiNNaker machine which will be used to simulate Nengo
# models.

[spinnaker_machine]
hostname: <host name of the machine here>
width: <width of the machine here>
height: <height of the machine here>

# Required parameters are:
# - hostname: (string) either the hostname or the IP address of the board
# containing chip (0, 0).
# - width: (int) width of the machine (0 <= width < 256)
# - height: (int) height of the machine (0 <= height < 256)
#
# Optional parameters are:
# - "hardware_version: (int) Version number of the SpiNNaker boards
# used in the system (e.g. SpiNN-5 boards would be 5). At the
# time of writing this value is ignored and can be safely set to
# the default value of 0.
# - "led_config": (int) Defines LED pin numbers for the SpiNNaker boards
# used in the system. The four least significant bits (3:0) give
# the number of LEDs. The next four bits give the pin number of the
# first LED, the next four the pin number of the second LED, and so
# forth. At the time of writing, all SpiNNaker board versions have
# their first LED attached to pin 0 and thus the default value of
# 0x00000001 is safe.
#
# For a Spin3 board connected to 192.168.240.253 this section would look
# like:
#
# hostname: 192.168.240.253
# width: 2
# height: 2
# hardware_version: 3
# led_config: 0x00000502
#
# For a Spin5 board connected to 192.168.1.1 this section would look
# like:
#
# hostname: 192.168.1.1
# width: 8
# height: 8
# hardware_version: 5
# led_config: 0x00000001

Empty file.
80 changes: 80 additions & 0 deletions nengo_spinnaker/scripts/nengo_spinnaker_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""A script which helps users create their nengo_spinnaker config file."""

import argparse

from nengo_spinnaker.utils.paths import nengo_spinnaker_rc

from rig import wizard

import os


CONFIG_TEMPLATE = """\
### SpiNNaker system configuration.
# File automatically generated by nengo_spinnaker_setup.
[spinnaker_machine]
hostname: {hostname}
width: {width}
height: {height}
"""


def generate_config_file(filename, dimensions, ip_address):
"""Generate a new config file with the specified filename and
parameters.
"""
try:
os.makedirs(os.path.dirname(filename))
except (IOError, OSError):
# Directory already created, good job!
pass

with open(filename, "w") as f:
f.write(CONFIG_TEMPLATE.format(
hostname=ip_address,
width=dimensions[0],
height=dimensions[1],
))


def main(args=None):
parser = argparse.ArgumentParser(
description="Interactive tool for creating a nengo_spinnaker "
"config file.")

file_group = parser.add_mutually_exclusive_group()
file_group.add_argument("--user", "-u", dest="file",
const="user", action="store_const",
help="Create a user-specific config file "
"(default).")
file_group.add_argument("--project", "-p", dest="file",
const="project", action="store_const",
help="Create a project-specific config file in "
"the current directory.")
file_group.set_defaults(file="user")

parser.add_argument("--force", "-f", action="store_true",
help="Overwrite an existing config file.")

args = parser.parse_args(args)

filename = nengo_spinnaker_rc[args.file]

if not args.force and os.path.isfile(filename):
print("Config file {} already exists. Use --force to "
"overwrite.".format(filename))
return 1

resp = wizard.cli_wrapper(wizard.cat(wizard.dimensions_wizard(),
wizard.ip_address_wizard()))

if resp is None:
return 1
else:
generate_config_file(filename, **resp)
print("Successfully created config file in {}".format(filename))
return 0

if __name__ == "__main__": # pragma: no cover
import sys
sys.exit(main())
9 changes: 8 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@ def get_new_url(url):
keywords="spinnaker nengo neural cognitive simulation",

# Requirements
install_requires=["nengo>=2.0.0, <3.0.0", "rig>=0.4.0, <1.0.0",
install_requires=["nengo>=2.0.0, <3.0.0", "rig>=0.5.3, <1.0.0",
"bitarray>=0.8.1, <1.0.0"],
zip_safe=False, # Partly for performance reasons

# Scripts
entry_points={
"console_scripts": [
"nengo_spinnaker_setup = nengo_spinnaker.scripts.nengo_spinnaker_setup:main",
],
}
)
87 changes: 87 additions & 0 deletions tests/scripts/test_nengo_spinnaker_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pytest

from mock import Mock

import tempfile

import os

import shutil

from nengo_spinnaker.scripts.nengo_spinnaker_setup \
import main, generate_config_file

from nengo_spinnaker.utils.paths import nengo_spinnaker_rc

from rig import wizard


def test_bad_args():
# Should fail if more than one config file is specified
with pytest.raises(SystemExit):
main("--project --user".split())


def test_generate_config_file():
# Config file generation should work straight-forwardly
fileno, filename = tempfile.mkstemp()
print(filename)

generate_config_file(filename, dimensions=(4, 8),
ip_address="127.0.0.1")

with open(filename, "r") as f:
config = f.read()
os.remove(filename)

assert "[spinnaker_machine]\n" in config
assert "hostname: 127.0.0.1\n" in config
assert "width: 4\n" in config
assert "height: 8\n" in config


def test_bad_main(monkeypatch):
# Check that when questions aren't answered right, the program exits with a
# failing status.
mock_cli_wrapper = Mock(return_value=None)
monkeypatch.setattr(wizard, "cli_wrapper", mock_cli_wrapper)

assert main("-pf".split()) != 0


def test_main(monkeypatch):
# Check that questions are asked and a config file generated
mock_cli_wrapper = Mock(return_value={"dimensions": (2, 2),
"ip_address": "127.0.0.1"})
monkeypatch.setattr(wizard, "cli_wrapper", mock_cli_wrapper)

# Temporarily any existing project config file out of the way
config = nengo_spinnaker_rc["project"]
if os.path.isfile(config): # pragma: no cover
_, temp = tempfile.mkstemp()
print(config, temp)
shutil.move(config, temp)
else:
temp = None

# Create a project config file in the test runner's directory (which
# shouldn't exist yet).
assert main("-p".split()) == 0
assert mock_cli_wrapper.called
mock_cli_wrapper.reset_mock()
assert os.path.isfile(config)

# Should fail to create a config file when one already exists
assert main("-p".split()) != 0
assert not mock_cli_wrapper.called

# ...unless forced
assert main("-p --force".split()) == 0
assert mock_cli_wrapper.called
mock_cli_wrapper.reset_mock()

# Restore the old config file
if temp is not None: # pragma: no cover
shutil.move(temp, config)
else:
os.remove(config)

0 comments on commit 01179c2

Please sign in to comment.