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
Classes:
|
Enum that lists available scopes and groups for prefs |
Data:
Ordered Dictionary containing default values for prefs. |
Functions:
|
Get a pref! |
|
Set a pref! |
|
Dump prefs into the |
|
Initialize prefs on autopilot start. |
|
Add a pref after init |
|
Get the git hash of the current commit. |
|
|
-
class
Scopes
(value)[source]¶ Bases:
enum.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:
All agents
Prefs specific to Terminal Agents
Prefs specific to Pilot Agents
Directory structure
Prefs for coordinating network between pilots and children
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 0x7f51eb60bc90>¶ 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.3.4', 'scope': <Scopes.COMMON: 1>, 'text': 'Location of virtual environment, if used.', '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', '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.3.4'), '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'}), ( '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'}), ( 'LINEAGE', { 'choices': ('NONE', 'PARENT', 'CHILD'), 'scope': <Scopes.LINEAGE: 5>, 'text': 'Are we a parent or a child?', 'type': 'choice'}), ( 'CHILDID', { 'depends': ('LINEAGE', 'PARENT'), 'scope': <Scopes.LINEAGE: 5>, 'text': 'Child ID:', 'type': 'str'}), ( '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', 'scope': <Scopes.AUDIO: 6>, 'text': 'Number of Audio channels', 'type': 'int'}), ( 'OUTCHANNELS', { 'default': '[1]', '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'}), ( 'JACKDSTRING', { 'default': 'jackd -P75 -p16 -t2000 -dalsa ' '-dhw:sndrpihifiberry -P -rfs -n3 -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. }
-
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
, ifNone
, return all prefs- Returns
value of pref (type variable!), or
None
if no pref of passedkey
-
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 resolveprefs.json
according theto 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 islook 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