prefs

Module to hold module-global variables as preferences.

Upon import, prefs attempts to import a prefs.json file from the default location (see prefs.init() ).

Prefs are then accessed with prefs.get() and prefs.set() functions. After initialization, if a pref if set, it is stored in the prefs.json file – prefs are semi-durable and persist across sessions.

When attempting to get a pref that is not set, prefs.get() will first try to find a default value (set in _PREFS , and if none is found return None – accordingly no prefs should be intentionally set to None, as it signifies that the pref is not set.

Prefs are thread- and process-safe, as they are stored and served by a multiprocessing.Manager object.

prefs.json is typically generated by running autopilot.setup.setup_autopilot , though you can freestyle it if you are so daring.

The ``HARDWARE`` pref is a little special. It specifies how each of the hardware components connected to the system is configured. It is a dictionary with this general structure:

'HARDWARE': {
    'GROUP': {
        'ID': {
            'hardware_arg': 'val'
        }
    }
}

where there are user-named 'GROUPS' of hardware objects, like 'LEDS' , etc. Within a group, each object has its 'ID' (passed as the name argument to the hardware initialization method) which allows it to be identified from the other components in the group. The intention of this structure is to allow multiple categories of hardware objects to be parameterized and used separately, even though they might be the same object type. Eg. we may have three LEDs in our nosepokes, but also have an LED that serves at the arena light. If we wanted to write a command that turns off all LEDs, we would have to explicitly specify their IDs, making it difficult to re-use very common hardware command patterns within tasks. There are obvious drawbacks to this scheme – clunky, ambiguous, etc. and will be deprecated as parameterization continues to congeal across the library.

The class that each element is used with is determined by the Task.HARDWARE dictionary. Specifically, the Task.init_hardware() method does something like:

self.hardware['GROUP']['ID'] = self.HARDWARE['GROUP']['ID'](**prefs.get('HARDWARE')['GROUP']['ID'])

Warning

These are not hard coded prefs. _DEFAULTS populates the default values for prefs, but local prefs are always restored from and saved to prefs.json . If you’re editing this file and things aren’t changing, you’re in the wrong place!

This iteration of prefs with respect to work done on the People’s Ventilator Project

If a pref has a string for a 'deprecation' field in prefs._DEFAULTS , a FutureWarning will be raised with the string given as the message

Classes:

Scopes(value)

Enum that lists available scopes and groups for prefs

Data:

_DEFAULTS

Ordered Dictionary containing default values for prefs.

_WARNED

Keep track of which prefs we have warned about getting defaults for so we don't warn a zillion times

Functions:

get([key])

Get a pref!

set(key, val)

Set a pref!

save_prefs([prefs_fn])

Dump prefs into the prefs_fn .json file

init([fn])

Initialize prefs on autopilot start.

add(param, value)

Add a pref after init

git_version(repo_dir)

Get the git hash of the current commit.

compute_calibration([path, calibration, ...])

Parameters
  • path

clear()

Mostly for use in testing, clear loaded prefs (without deleting prefs.json)

class Scopes(value)[source]

Bases: Enum

Enum that lists available scopes and groups for prefs

Scope can be an agent type, common (for everyone), or specify some subgroup of prefs that should be presented together (like directories)

COMMON = All Agents DIRECTORY = Prefs group for specifying directory structure TERMINAL = prefs for Terminal Agents Pilot = Prefs for Pilot agents LINEAGE = prefs for networking lineage (until networking becomes more elegant ;) AUDIO = Prefs for configuring the Jackd audio server

Attributes:

COMMON

All agents

TERMINAL

Prefs specific to Terminal Agents

PILOT

Prefs specific to Pilot Agents

DIRECTORY

Directory structure

LINEAGE

Prefs for coordinating network between pilots and children

AUDIO

Audio prefs...

COMMON = 1

All agents

TERMINAL = 2

Prefs specific to Terminal Agents

PILOT = 3

Prefs specific to Pilot Agents

DIRECTORY = 4

Directory structure

LINEAGE = 5

Prefs for coordinating network between pilots and children

AUDIO = 6

Audio prefs…

_PREF_MANAGER = <multiprocessing.managers.SyncManager object at 0x7fc26f3a7950>

The multiprocessing.Manager that stores prefs during system operation and makes them available and consistent across processes.

_DEFAULTS = OrderedDict([   (   'NAME',                     {   'scope': <Scopes.COMMON: 1>,                         'text': 'Agent Name:',                         'type': 'str'}),                 (   'PUSHPORT',                     {   'default': '5560',                         'scope': <Scopes.COMMON: 1>,                         'text': 'Push Port - Router port used by the Terminal '                                 'or upstream agent:',                         'type': 'int'}),                 (   'MSGPORT',                     {   'default': '5565',                         'scope': <Scopes.COMMON: 1>,                         'text': 'Message Port - Router port used by this agent '                                 'to receive messages:',                         'type': 'int'}),                 (   'TERMINALIP',                     {   'default': '192.168.0.100',                         'scope': <Scopes.COMMON: 1>,                         'text': 'Terminal IP:',                         'type': 'str'}),                 (   'LOGLEVEL',                     {   'choices': ('DEBUG', 'INFO', 'WARNING', 'ERROR'),                         'default': 'WARNING',                         'scope': <Scopes.COMMON: 1>,                         'text': 'Log Level:',                         'type': 'choice'}),                 (   'LOGSIZE',                     {   'default': 5242880,                         'scope': <Scopes.COMMON: 1>,                         'text': 'Size of individual log file (in bytes)',                         'type': 'int'}),                 (   'LOGNUM',                     {   'default': 4,                         'scope': <Scopes.COMMON: 1>,                         'text': 'Number of logging backups to keep of LOGSIZE',                         'type': 'int'}),                 (   'CONFIG',                     {   'hidden': True,                         'scope': <Scopes.COMMON: 1>,                         'text': 'System Configuration',                         'type': 'list'}),                 (   'VENV',                     {   'default': '/home/docs/checkouts/readthedocs.org/user_builds/auto-pi-lot/envs/v0.4.4.post0',                         'scope': <Scopes.COMMON: 1>,                         'text': 'Location of virtual environment, if used.',                         'type': 'str'}),                 (   'AUTOPLUGIN',                     {   'default': True,                         'scope': <Scopes.COMMON: 1>,                         'text': 'Attempt to import the contents of the plugin '                                 'directory',                         'type': 'bool'}),                 (   'PLUGIN_DB',                     {   'default': '/home/docs/autopilot/plugin_db.json',                         'scope': <Scopes.COMMON: 1>,                         'text': 'filename to use for the .json plugin_db that '                                 'keeps track of installed plugins',                         'type': 'str'}),                 (   'BASEDIR',                     {   'default': '/home/docs/autopilot',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Base Directory',                         'type': 'str'}),                 (   'DATADIR',                     {   'default': '/home/docs/autopilot/data',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Data Directory',                         'type': 'str'}),                 (   'SOUNDDIR',                     {   'default': '/home/docs/autopilot/sounds',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Sound file directory',                         'type': 'str'}),                 (   'LOGDIR',                     {   'default': '/home/docs/autopilot/logs',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Log Directory',                         'type': 'str'}),                 (   'VIZDIR',                     {   'default': '/home/docs/autopilot/viz',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Directory to store Visualization results',                         'type': 'str'}),                 (   'PROTOCOLDIR',                     {   'default': '/home/docs/autopilot/protocols',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Protocol Directory',                         'type': 'str'}),                 (   'PLUGINDIR',                     {   'default': '/home/docs/autopilot/plugins',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Directory to import ',                         'type': 'str'}),                 (   'REPODIR',                     {   'default': PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/auto-pi-lot/checkouts/v0.4.4.post0'),                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Location of Autopilot repo/library',                         'type': 'str'}),                 (   'CALIBRATIONDIR',                     {   'default': '/home/docs/autopilot/calibration',                         'scope': <Scopes.DIRECTORY: 4>,                         'text': 'Location of calibration files for solenoids, '                                 'etc.',                         'type': 'str'}),                 (   'PIGPIOMASK',                     {   'default': '1111110000111111111111110000',                         'scope': <Scopes.PILOT: 3>,                         'text': 'Binary mask controlling which pins pigpio '                                 'controls according to their BCM numbering, '                                 'see the -x parameter of pigpiod',                         'type': 'str'}),                 (   'PIGPIOARGS',                     {   'default': '-t 0 -l',                         'scope': <Scopes.PILOT: 3>,                         'text': 'Arguments to pass to pigpiod on startup',                         'type': 'str'}),                 (   'PULLUPS',                     {   'scope': <Scopes.PILOT: 3>,                         'text': 'Pins to pull up on system startup? (list of '                                 'form [1, 2])',                         'type': 'list'}),                 (   'PULLDOWNS',                     {   'scope': <Scopes.PILOT: 3>,                         'text': 'Pins to pull down on system startup? (list of '                                 'form [1, 2])',                         'type': 'list'}),                 (   'PING_INTERVAL',                     {   'default': 5,                         'scope': <Scopes.PILOT: 3>,                         'text': 'How many seconds should pilots wait in '                                 'between pinging the Terminal?',                         'type': 'float'}),                 (   'DRAWFPS',                     {   'default': '20',                         'scope': <Scopes.TERMINAL: 2>,                         'text': 'FPS to draw videos displayed during '                                 'acquisition',                         'type': 'int'}),                 (   'PILOT_DB',                     {   'default': '/home/docs/autopilot/pilot_db.json',                         'scope': <Scopes.TERMINAL: 2>,                         'text': 'filename to use for the .json pilot_db that '                                 'maps pilots to subjects (relative to BASEDIR)',                         'type': 'str'}),                 (   'TERMINAL_SETTINGS_FN',                     {   'default': '/home/docs/autopilot/terminal.conf',                         'scope': <Scopes.TERMINAL: 2>,                         'text': 'filename to store QSettings file for Terminal',                         'type': 'str'}),                 (   'TERMINAL_WINSIZE_BEHAVIOR',                     {   'choices': (   'remember',                                        'moderate',                                        'maximum',                                        'custom'),                         'default': 'remember',                         'scope': <Scopes.TERMINAL: 2>,                         'text': 'Strategy for resizing terminal window on '                                 'opening',                         'type': 'choice'}),                 (   'TERMINAL_CUSTOM_SIZE',                     {   'default': [0, 0, 1000, 400],                         'depends': ('TERMINAL_WINSIZE_BEHAVIOR', 'custom'),                         'scope': <Scopes.TERMINAL: 2>,                         'text': 'Custom size for window, specified as [px from '                                 'left, px from top, width, height]',                         'type': 'list'}),                 (   'LINEAGE',                     {   'choices': ('NONE', 'PARENT', 'CHILD'),                         'scope': <Scopes.LINEAGE: 5>,                         'text': 'Are we a parent or a child?',                         'type': 'choice'}),                 (   'CHILDID',                     {   'default': [],                         'depends': ('LINEAGE', 'PARENT'),                         'scope': <Scopes.LINEAGE: 5>,                         'text': 'List of Child ID:',                         'type': 'list'}),                 (   'PARENTID',                     {   'depends': ('LINEAGE', 'CHILD'),                         'scope': <Scopes.LINEAGE: 5>,                         'text': 'Parent ID:',                         'type': 'str'}),                 (   'PARENTIP',                     {   'depends': ('LINEAGE', 'CHILD'),                         'scope': <Scopes.LINEAGE: 5>,                         'text': 'Parent IP:',                         'type': 'str'}),                 (   'PARENTPORT',                     {   'depends': ('LINEAGE', 'CHILD'),                         'scope': <Scopes.LINEAGE: 5>,                         'text': 'Parent Port:',                         'type': 'str'}),                 (   'AUDIOSERVER',                     {   'scope': <Scopes.AUDIO: 6>,                         'text': 'Enable jack audio server?',                         'type': 'bool'}),                 (   'NCHANNELS',                     {   'default': 1,                         'depends': 'AUDIOSERVER',                         'deprecation': 'Deprecated and will be removed, use '                                        'OUTCHANNELS instead',                         'scope': <Scopes.AUDIO: 6>,                         'text': 'Number of Audio channels (deprecated; used '                                 'OUTCHANNELS)',                         'type': 'int'}),                 (   'OUTCHANNELS',                     {   'default': '',                         'depends': 'AUDIOSERVER',                         'scope': <Scopes.AUDIO: 6>,                         'text': 'List of Audio channel indexes to connect to',                         'type': 'list'}),                 (   'FS',                     {   'default': 192000,                         'depends': 'AUDIOSERVER',                         'scope': <Scopes.AUDIO: 6>,                         'text': 'Audio Sampling Rate',                         'type': 'int'}),                 (   'ALSA_NPERIODS',                     {   'default': 3,                         'depends': 'AUDIOSERVER',                         'scope': <Scopes.AUDIO: 6>,                         'text': 'number of buffer periods to use with ALSA '                                 'sound driver',                         'type': 'int'}),                 (   'JACKDSTRING',                     {   'default': 'jackd -P75 -p16 -t2000 -dalsa '                                    '-dhw:sndrpihifiberry -P -rfs -nper -s &',                         'depends': 'AUDIOSERVER',                         'scope': <Scopes.AUDIO: 6>,                         'text': 'Arguments to pass to jackd, see the jackd '                                 'manpage',                         'type': 'str'})])

Ordered Dictionary containing default values for prefs.

An Ordered Dictionary lets the prefs be displayed in gui elements in a predictable order, but prefs are stored in prefs.json in alphabetical order and the ‘live’ prefs used during runtime are stored in _PREFS

Each entry should be a dict with the following structure:

"PREF_NAME": {
    "type": (str, int, bool, choice, list) # specify the appropriate GUI input, str or int are validators,
    choices are a
        # dropdown box, and lists allow users to specify lists of values like "[0, 1]"
    "default": If possible, assign default value, otherwise None
    "text": human-readable text that described the pref
    "scope": to whom does this pref apply? see :class:`.Scopes`
    "depends": name of another pref that needs to be supplied/enabled for this one to be enabled (eg. don't set sampling rate of audio server if audio server disabled)
        can also be specified as a tuple like ("LINEAGE", "CHILD") that enables the option when prefs[depends[0]] == depends[1]
    "choices": If type=="choice", a tuple of available choices.
}
_WARNED = []

Keep track of which prefs we have warned about getting defaults for so we don’t warn a zillion times

get(key: Optional[str] = None)[source]

Get a pref!

If a value for the given key can’t be found, prefs will attempt to

Parameters

key (str, None) – get pref of specific key, if None, return all prefs

Returns

value of pref (type variable!), or None if no pref of passed key

set(key: str, val)[source]

Set a pref!

Note

Whenever a pref is set, the prefs file is automatically updated – prefs are system-durable!!

(specifically, whenever the module-level _INITIALIZED value is set to True, prefs are saved to file to avoid overwriting before loading)

Parameters
  • key (str) – Name of pref to set

  • val – Value of pref to set (prefs are not type validated against default types)

save_prefs(prefs_fn: Optional[str] = None)[source]

Dump prefs into the prefs_fn .json file

Parameters
  • prefs_fn (str, None) – if provided, pathname to prefs.json otherwise resolve prefs.json according the

  • to the normal methods….

init(fn=None)[source]

Initialize prefs on autopilot start.

If passed dict of prefs or location of prefs.json, load and use that

Otherwise

  • Look for the autopilot wayfinder ~/.autopilot file that tells us where the user directory is

  • look in default location ~/autopilot/prefs.json

Todo

This function may be deprecated in the future – in its current form it serves to allow the sorta janky launch methods in the headers/footers of autopilot/core/pilot.py and autopilot/core/terminal.py that will eventually be transformed into a unified agent framework to make launching easier. Ideally one would be able to just import prefs without having to explicitly initialize it, but we need to formalize the full launch process before we make the full lurch to that model.

Parameters

fn (str, dict) – a path to prefs.json or a dictionary of preferences

add(param, value)[source]

Add a pref after init

Parameters
  • param (str) – Allcaps parameter name

  • value – Value of the pref

git_version(repo_dir)[source]

Get the git hash of the current commit.

Stolen from numpy’s setup

and linked by ryanjdillon on SO

Parameters

repo_dir (str) – directory of the git repository.

Returns

git commit hash.

Return type

unicode

compute_calibration(path=None, calibration=None, do_return=False)[source]
Parameters
  • path

  • calibration

  • do_return

Returns:

clear()[source]

Mostly for use in testing, clear loaded prefs (without deleting prefs.json)

(though you will probably overwrite prefs.json if you clear and then set another pref so don’t use this except in testing probably)