import argparse
import os
import json
import logging
"""
Plugin Configurations.
Load settings from different sources, and combine them by priority.
Order of priority for settings:
1) Highest priority are CLI args used at runtime.
2) Then environment variables.
3) Finally, fall back on config file setup with `nanome-setup-plugins` command.
Fetchable Settings
host NTS host or ip address
port NTS port
key Specifies a key file or key string to use to connect to NTS
verbose Enable verbose mode, to display Logs.debug
name Name to display for this plugin in Nanome
write_log_file Enable or disable writing logs to .log file
remote_logging Toggle whether or not logs should be forwarded to NTS.
auto_reload Restart plugin automatically if a .py or .json file in current directory changes
ignore To use with auto-reload. All paths matching this pattern will be ignored,
use commas to specify several. Supports */?/[seq]/[!seq]
Environment Variable settings keys.
NTS_HOST
NTS_PORT
NTS_KEY
PLUGIN_NAME
PLUGIN_VERBOSE
PLUGIN_WRITE_LOG_FILE
PLUGIN_REMOTE_LOGGING
PLUGIN_AUTO_RELOAD
PLUGIN_IGNORE
"""
__all__ = ['load_settings', 'fetch', 'set', 'create_parser']
logger = logging.getLogger(__name__)
def str2bool(v):
"""Accept various truthy/falsey values as boolean arguments."""
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
[docs]def create_parser():
"""Command Line Interface for Plugins.
rtype: argsparser: args parser
"""
parser = argparse.ArgumentParser(description='Starts a Nanome Plugin.')
parser.add_argument('-a', '--host', help='connects to NTS at the specified IP address')
parser.add_argument('-p', '--port', type=int, help='connects to NTS at the specified port')
parser.add_argument('-k', '--keyfile', dest='key', default='', help='Specifies a key file or key string to use to connect to NTS')
parser.add_argument('-n', '--name', help='Name to display for this plugin in Nanome', default='')
parser.add_argument('-v', '--verbose', action='store_true', default=None, help='enable verbose mode, to display Logs.debug')
parser.add_argument('--write-log-file', type=str2bool, default=None, dest='write_log_file', help='Enable or disable writing logs to .log file')
parser.add_argument('--remote-logging', type=str2bool, default=None, dest='remote_logging', help='Toggle whether or not logs should be forwarded to NTS.')
parser.add_argument('-r', '--auto-reload', action='store_true', default=None, dest='auto_reload', help='Restart plugin automatically if a .py or .json file in current directory changes')
parser.add_argument('-i', '--ignore', help='To use with auto-reload. All paths matching this pattern will be ignored, use commas to specify several. Supports */?/[seq]/[!seq]', default='')
return parser
[docs]def load_settings():
"""Load settings from different sources, and combine them by priority.
Order of priority for settings:
1) Highest priority are CLI args used at runtime.
2) Then environment variables.
3) Finally, fall back on config file.
"""
config_dict = _get_config_dict()
cli_dict = _get_cli_args()
environ_dict = _get_environ_dict()
plugin_settings = dict(config_dict)
plugin_settings.update(environ_dict)
plugin_settings.update(cli_dict)
return plugin_settings
[docs]def fetch(key):
"""
| Fetch a configuration value
| Built-in keys are:
| host - your NTS server address
| port - your NTS server port
| key - your NTS key file or string
| plugin_files_path - where your plugins will store files
| write_log_file - if this is True, plugin will write Logs to a local .log file
:param key: The key of the config value to fetch
:type key: :class:`str`
"""
plugin_settings = load_settings()
return plugin_settings.get(key)
[docs]def set(key, value):
"""
| Set a configuration entry in your nanome configuration.
| Built-in keys are host, port, key and plugin_files_path.
:param key: The key of the config value to set
:type key: :class:`str`
:param value: The value to set the config item to
:type value: :class:`str`
"""
config_path = _get_config_path()
with open(config_path, "r") as file:
config_json = json.load(file)
config_json[key] = value
with open(config_path, "w") as file:
json.dump(config_json, file)
return True
def _get_config_dict():
config_path = _setup_file()
if config_path:
with open(config_path, "r") as f:
config_dict = json.load(f)
else:
logger.warning("Could not write config file, please check permissions.")
config_dict = {}
serialized_dict = _serialize_dict_with_parser(config_dict)
return serialized_dict
def _get_environ_dict():
"""Get values set by environment variables."""
environ_dict = {
'host': os.environ.get('NTS_HOST'),
'port': os.environ.get('NTS_PORT'),
'key': os.environ.get('NTS_KEY'),
'name': os.environ.get('PLUGIN_NAME'),
'write_log_file': os.environ.get('PLUGIN_WRITE_LOG_FILE'),
'remote_logging': os.environ.get('PLUGIN_REMOTE_LOGGING'),
'auto_reload': os.environ.get('PLUGIN_AUTO_RELOAD'),
'ignore': os.environ.get('PLUGIN_IGNORE'),
}
# Only add verbose key if it is explicitly true (its a store_true arg)
verbose = str2bool(os.environ.get('PLUGIN_VERBOSE', False))
if verbose:
environ_dict['verbose'] = verbose
serialized_dict = _serialize_dict_with_parser(environ_dict)
return serialized_dict
def _get_cli_args():
parser = create_parser()
cli_dict = vars(parser.parse_known_args()[0])
for k in list(cli_dict.keys()):
if cli_dict[k] in [None, '']:
cli_dict.pop(k)
return cli_dict
def _setup_file():
config_path = _get_config_path()
directory = os.path.dirname(config_path)
if not os.path.isdir(directory):
try:
os.mkdir(directory)
except Exception:
return False
if not os.path.isfile(config_path):
try:
logger.info("Creating config file with path " + config_path)
_setup_clean_config(config_path)
except Exception:
return False
return config_path
def _get_config_path():
s = "/"
home = os.getenv('APPDATA')
if home is None:
home = os.getenv('HOME')
directory = home + s + ".nanome_lib"
config_path = directory + s + "config.txt"
return config_path
def _setup_clean_config(config_path):
default_json = {
"host": "",
"port": "8888",
"key": "",
"plugin_files_path": "~/Documents/nanome-plugins",
"write_log_file": True,
}
with open(config_path, "w") as file:
json.dump(default_json, file)
def _serialize_dict_with_parser(args_dict):
"""Use cli parser to format args as correct data types."""
parser = create_parser()
for action in parser._actions:
field_name = action.dest
if field_name in args_dict and args_dict[field_name] not in [None, '']:
arg_list = [action.option_strings[0], str(args_dict[field_name])]
ns, _ = parser.parse_known_args(arg_list)
args_dict[field_name] = getattr(ns, field_name)
args_dict = {k: v for k, v in args_dict.items() if v not in [None, '']}
return args_dict