#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=eval-used,protected-access
"""
:mod:`orion.core.cli.insert` -- Module to insert new trials
===========================================================
.. module:: insert
:platform: Unix
:synopsis: Insert creates new trials for a given experiment with fixed values
"""
import collections
import logging
import os
import re
from orion.core.cli import base as cli
from orion.core.io.convert import infer_converter_from_file_type
import orion.core.io.experiment_builder as experiment_builder
from orion.core.utils.format_trials import tuple_to_trial
log = logging.getLogger(__name__)
[docs]def add_subparser(parser):
"""Add the subparser that needs to be used for this command"""
insert_parser = parser.add_parser('insert', help='insert help')
cli.get_basic_args_group(insert_parser)
cli.get_user_args_group(insert_parser)
insert_parser.set_defaults(func=main)
insert_parser.set_defaults(help_empty=True) # Print help if command is empty
return insert_parser
[docs]def main(args):
"""Fetch config and insert new point"""
command_line_user_args = args.pop('user_args', [None])[1:]
experiment_view = experiment_builder.build_view_from_args(args)
experiment = experiment_builder.build(name=experiment_view.name,
version=experiment_view.version)
transformed_args = _build_from(command_line_user_args)
exp_space = experiment.space
values = _create_tuple_from_values(transformed_args, exp_space)
trial = tuple_to_trial(values, exp_space)
experiment.register_trial(trial)
def _validate_dimensions(transformed_args, exp_space):
exp_namespaces = list(exp_space.keys())
values = {}
# Find any dimension that is not given by the user and make sure they have a default value
for exp_n in exp_namespaces:
if exp_n not in transformed_args.keys() and exp_space[exp_n].default_value is None:
error_msg = "Dimension {} is unspecified and has no default value".format(exp_n)
raise ValueError(error_msg)
# Find any namespace that is not in the space of the experiment,
# or values that lie outside the prior's interval
for namespace, value in transformed_args.items():
if namespace not in exp_space:
error_msg = "Found namespace outside of experiment space : {}".format(namespace)
raise ValueError(error_msg)
valid, value = _validate_input_value(value, exp_space, namespace)
if not valid:
error_msg = "Value {} is outside of dimension's prior interval".format(value)
raise ValueError(error_msg)
values[namespace] = value
return values
def _validate_input_value(value, exp_space, namespace):
is_valid = False
try:
value = eval(value)
is_valid = value in exp_space[namespace]
except NameError:
is_valid = value in exp_space[namespace]
return is_valid, value
def _create_tuple_from_values(transformed_args, exp_space):
values_dict = _validate_dimensions(transformed_args, exp_space)
values = []
for namespace in exp_space:
if namespace in values_dict.keys():
values.append(values_dict[namespace])
else:
values.append(exp_space[namespace].default_value)
return values
def _build_from(cmd_args):
transformed_args, userconfig, _ = _build_from_args(cmd_args)
if userconfig:
transformed_args = _build_from_config(userconfig)
return transformed_args
def _build_from_config(config_path):
converter = infer_converter_from_file_type(config_path)
userconfig_tmpl = converter.parse(config_path)
userconfig_keyword = 'orion='
transformed_args = {}
stack = collections.deque()
stack.append(('', userconfig_tmpl))
while True:
try:
namespace, stuff = stack.pop()
except IndexError:
break
if isinstance(stuff, dict):
for k, v in stuff.items():
stack.append(('/'.join([namespace, str(k)]), v))
elif isinstance(stuff, list):
for position, thing in enumerate(stuff):
stack.append(('/'.join([namespace, str(position)]), thing))
elif isinstance(stuff, str):
if stuff.startswith(userconfig_keyword):
if namespace in transformed_args:
error_msg = "Conflict for name '{}' in script configuration "\
"and arguments.".format(namespace)
raise ValueError(error_msg)
transformed_args[namespace] = stuff[len(userconfig_keyword):]
return transformed_args
def _build_from_args(cmd_args):
userconfig = None
is_userconfig_an_option = None
userargs_search = r'\W*([a-zA-Z0-9_-]+)=([^ ]+)'
userargs_regex_tmpl = r'(.*)=(.*)'
userconfig_option = "--config="
transformed_args = {}
userargs_tmpl = collections.defaultdict(list)
args_pattern = re.compile(userargs_search)
args_prefix_pattern = re.compile(userargs_regex_tmpl)
for arg in cmd_args:
found = args_pattern.findall(arg)
if len(found) != 1:
if arg.startswith(userconfig_option):
if not userconfig:
userconfig = arg[len(userconfig_option):]
is_userconfig_an_option = True
else:
raise ValueError(
"Already found one configuration file in: %s" %
userconfig
)
else:
userargs_tmpl[None].append(arg)
continue
name, value = found[0]
namespace = '/' + name
if namespace in transformed_args:
error_msg = "Conflict for name '{}' in script configuration "\
"and arguments.".format(namespace)
raise ValueError(error_msg)
transformed_args[namespace] = value
found = args_prefix_pattern.findall(arg)
assert len(found) == 1 and found[0][1] == value, "Parsing prefix problem."
userargs_tmpl[namespace] = found[0][0] + '='
if not userconfig and userargs_tmpl[None]: # try the first positional argument
if os.path.isfile(userargs_tmpl[None][0]):
userconfig = userargs_tmpl[None].pop(0)
is_userconfig_an_option = False
return transformed_args, userconfig, is_userconfig_an_option