-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from project-rig/config-setup-script
Add nengo_spinnaker_setup command.
- Loading branch information
Showing
6 changed files
with
244 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |