Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a configuration default user configurable through verdi config #2734

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions aiida/backends/tests/cmdline/commands/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,22 @@ def test_config_option_unset(self):
result = self.cli_runner.invoke(cmd_verdi.verdi, options)
self.assertClickSuccess(result)
self.assertEqual(result.output, '')

@with_temporary_config_instance
def test_config_option_set_global_only(self):
"""Test that `global_only` options are only set globally even if the `--global` flag is not set."""
config = get_config()
option_name = 'user.email'
option_value = 'some@email.com'

options = ['config', option_name, str(option_value)]
result = self.cli_runner.invoke(cmd_verdi.verdi, options)
self.assertClickSuccess(result)

options = ['config', option_name]
result = self.cli_runner.invoke(cmd_verdi.verdi, options)

# Check that the current profile name is not in the output
self.assertClickSuccess(result)
self.assertIn(option_value, result.output.strip())
self.assertNotIn(config.current_profile.name, result.output.strip())
16 changes: 16 additions & 0 deletions aiida/backends/tests/manage/configuration/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,22 @@ def test_option(self):
config.option_set(option_name, option_value)
self.assertEqual(config.option_get(option_name, scope=None, default=False), option_value)

def test_option_global_only(self):
"""Test that `global_only` options are only set globally even if a profile specific scope is set."""
option_name = 'user.email'
option_value = 'some@email.com'

config = Config(self.config_filepath, self.config_dictionary)

# Setting an option globally should be fine
config.option_set(option_name, option_value, scope=None)
self.assertEqual(config.option_get(option_name, scope=None, default=False), option_value)

# Setting an option profile specific should actually not set it on the profile since it is `global_only`
config.option_set(option_name, option_value, scope=None)
self.assertEqual(config.option_get(option_name, scope=self.profile_name, default=False), None)
self.assertEqual(config.option_get(option_name, scope=None, default=False), option_value)

def test_store(self):
"""Test that the store method writes the configuration properly to disk."""
config = Config(self.config_filepath, self.config_dictionary)
Expand Down
3 changes: 3 additions & 0 deletions aiida/cmdline/commands/cmd_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ def verdi_config(ctx, option, value, globally, unset):
config = ctx.obj.config
profile = ctx.obj.profile

if option.global_only:
globally = True

# Define the string that determines the scope: for specific profile or globally
scope = profile.name if (not globally and profile) else None
scope_text = 'for {}'.format(profile.name) if (not globally and profile) else 'globally'
Expand Down
2 changes: 1 addition & 1 deletion aiida/manage/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def get_config_option(option_name):
try:
config = get_config()
except exceptions.ConfigurationError:
value = option.default
value = option.default if option.default is not options.NO_DEFAULT else None
else:
if config.current_profile:
# Try to get the option for the profile, but do not return the option default
Expand Down
7 changes: 5 additions & 2 deletions aiida/manage/configuration/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def option_set(self, option_name, option_value, scope=None):
"""
option, parsed_value = parse_option(option_name, option_value)

if scope is not None:
if not option.global_only and scope is not None:
dictionary = self._get_profile_dictionary(scope)
else:
dictionary = self.dictionary
Expand Down Expand Up @@ -283,7 +283,10 @@ def option_get(self, option_name, scope=None, default=True):
else:
dictionary = self.dictionary

return dictionary.get(option.key, option.default if default else None)
# Default value is `None` unless `default=True` and the `option.default` is not `NO_DEFAULT`
default_value = option.default if default and option.default is not NO_DEFAULT else None

return dictionary.get(option.key, default_value)

def store(self):
"""Write the current config to file."""
Expand Down
55 changes: 48 additions & 7 deletions aiida/manage/configuration/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,12 @@

__all__ = ('get_option', 'get_option_names', 'parse_option')


class NO_DEFAULT_PLACEHOLDER(object): # pylint: disable=too-few-public-methods,invalid-name,useless-object-inheritance
"""A dummy class to serve as a marker for no default being specified in the `get_option` function."""


NO_DEFAULT = NO_DEFAULT_PLACEHOLDER()
NO_DEFAULT = ()
DEFAULT_DAEMON_TIMEOUT = 20 # Default timeout in seconds for circus client calls
VALID_LOG_LEVELS = ['CRITICAL', 'ERROR', 'WARNING', 'REPORT', 'INFO', 'DEBUG']

Option = collections.namedtuple('Option', ['name', 'key', 'valid_type', 'valid_values', 'default', 'description'])
Option = collections.namedtuple('Option',
['name', 'key', 'valid_type', 'valid_values', 'default', 'description', 'global_only'])


def get_option(option_name):
Expand Down Expand Up @@ -96,89 +92,134 @@ def parse_option(option_name, option_value):
'valid_values': None,
'default': 1,
'description': 'The polling interval in seconds to be used by process runners',
'global_only': False,
},
'daemon.timeout': {
'key': 'daemon_timeout',
'valid_type': 'int',
'valid_values': None,
'default': DEFAULT_DAEMON_TIMEOUT,
'description': 'The timeout in seconds for calls to the circus client',
'global_only': False,
},
'verdi.shell.auto_import': {
'key': 'verdi_shell_auto_import',
'valid_type': 'string',
'valid_values': None,
'default': '',
'description': 'Additional modules/functions/classes to be automatically loaded in `verdi shell`',
'global_only': False,
},
'logging.aiida_loglevel': {
'key': 'logging_aiida_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'REPORT',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `aiida` logger',
'global_only': False,
},
'logging.db_loglevel': {
'key': 'logging_db_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'REPORT',
'description': 'Minimum level to log to the DbLog table',
'global_only': False,
},
'logging.tornado_loglevel': {
'key': 'logging_tornado_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `tornado` logger',
'global_only': False,
},
'logging.plumpy_loglevel': {
'key': 'logging_plumpy_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `plumpy` logger',
'global_only': False,
},
'logging.kiwipy_loglevel': {
'key': 'logging_kiwipy_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `kiwipy` logger',
'global_only': False,
},
'logging.paramiko_loglevel': {
'key': 'logging_paramiko_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `paramiko` logger',
'global_only': False,
},
'logging.alembic_loglevel': {
'key': 'logging_alembic_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `alembic` logger',
'global_only': False,
},
'logging.sqlalchemy_loglevel': {
'key': 'logging_sqlalchemy_loglevel',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'WARNING',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `sqlalchemy` logger',
'global_only': False,
},
'logging.circus_loglevel': {
'key': 'logging_circus_log_level',
'valid_type': 'string',
'valid_values': VALID_LOG_LEVELS,
'default': 'INFO',
'description': 'Minimum level to log to daemon log and the `DbLog` table for the `circus` logger',
'global_only': False,
},
'user.email': {
'key': 'user_email',
'valid_type': 'string',
'valid_values': None,
'default': NO_DEFAULT,
'description': 'Default user email to use when creating new profiles.',
'global_only': True,
},
'user.first_name': {
'key': 'user_first_name',
'valid_type': 'string',
'valid_values': None,
'default': NO_DEFAULT,
'description': 'Default user first name to use when creating new profiles.',
'global_only': True,
},
'user.last_name': {
'key': 'user_last_name',
'valid_type': 'string',
'valid_values': None,
'default': NO_DEFAULT,
'description': 'Default user last name to use when creating new profiles.',
'global_only': True,
},
'user.institution': {
'key': 'user_institution',
'valid_type': 'string',
'valid_values': None,
'default': NO_DEFAULT,
'description': 'Default user institution to use when creating new profiles.',
'global_only': True,
},
'warnings.showdeprecations': {
'key': 'show_deprecations',
'valid_type': 'bool',
'valid_values': None,
'default': True,
'description': 'Boolean whether to print AiiDA deprecation warnings',
'global_only': False,
},
}