def _expect(post_state: Dict[str, Any], networks: Any, transaction: TransactionDict, filler: Dict[str, Any]) -> Dict[str, Any]: test_name = get_test_name(filler) test = filler[test_name] test_update = {test_name: {}} # type: Dict[str, Dict[Any, Any]] pre_state = test.get("pre", {}) post_state = normalize_state(post_state or {}) defaults = {address: { "balance": 0, "nonce": 0, "code": b"", "storage": {}, } for address in post_state} result = deep_merge(defaults, pre_state, normalize_state(post_state)) new_expect = {"result": result} if transaction is not None: transaction = normalize_transaction( merge(get_default_transaction(networks), transaction) ) if "transaction" not in test: transaction_group = apply_formatters_to_dict({ "data": wrap_in_list, "gasLimit": wrap_in_list, "value": wrap_in_list, }, transaction) indexes = { index_key: 0 for transaction_key, index_key in [ ("gasLimit", "gas"), ("value", "value"), ("data", "data"), ] if transaction_key in transaction_group } else: transaction_group, indexes = add_transaction_to_group( test["transaction"], transaction ) new_expect = assoc(new_expect, "indexes", indexes) test_update = assoc_in(test_update, [test_name, "transaction"], transaction_group) if networks is not None: networks = normalize_networks(networks) new_expect = assoc(new_expect, "networks", networks) existing_expects = test.get("expect", []) expect = existing_expects + [new_expect] test_update = assoc_in(test_update, [test_name, "expect"], expect) return deep_merge(filler, test_update)
def parse_quinfig(self): # Parse all the arguments from the command line, overriding defaults in the argparse args = self.parse_args() cli_keys = [] for cli_arg in _sys.argv[1:]: if cli_arg.startswith('--'): if str(cli_arg) == '--config': continue cli_keys.append(cli_arg[2:].replace("-", "_")) elif cli_arg.startswith('-'): raise NotImplementedError( "QuinineArgumentParser doesn't support abbreviated arguments." ) else: continue # Get parameters which need to be overridden from command line override_args = project(args.__dict__, cli_keys) # Trick: first load the config without a schema as a base config # quinfig = Quinfig(config_path=args.config) # Override all the defaults using the yaml config # quinfig = rmerge(Quinfig(config=args.__dict__), quinfig) # print(quinfig) # Use all the defaults in args to populate a dictionary quinfig = {} for param, val in args.__dict__.items(): param_path = param.split(".") quinfig = tz.assoc_in(quinfig, param_path, val) # Override all the defaults using the yaml config quinfig = rmerge(quinfig, Quinfig(config_path=args.config)) # Replace all the arguments passed into command line if len(override_args) > 0: print( f"\n(quinine) Overriding parameters in {args.config} from command line (___ is unspecified)." ) for param, val in override_args.items(): param_path = param.split(".") old_val = tz.get_in(param_path, quinfig) if old_val != val: print(f"> ({param}): {old_val} --> {val}") else: print(f"> ({param}): ___ --> {val}") quinfig = tz.assoc_in(quinfig, param_path, val) # Load the config again return Quinfig(config=quinfig, schema=self.schema)
def fill_test(filler: Dict[str, Any], info: Dict[str, Any] = None, apply_formatter: bool = True, **kwargs: Any) -> Dict[str, Any]: test_name = get_test_name(filler) test = filler[test_name] if "transaction" in test: filled = fill_state_test(filler) formatter = filled_state_test_formatter elif "exec" in test: filled = fill_vm_test(filler, **kwargs) formatter = filled_vm_test_formatter else: raise ValueError( "Given filler does not appear to be for VM or state test") info = merge( { "filledwith": FILLED_WITH_TEMPLATE.format(version=get_version_from_git()) }, info if info else {}) filled = assoc_in(filled, [test_name, "_info"], info) if apply_formatter: return formatter(filled) else: return filled
def run_args(args): if args.config is not None: with open(args.config) as f: config = yaml.load(f) else: config = { 'basecall': {}, 'version': "v0.1", } for k, v in vars(args).items(): if v is not None and "." in k: config = toolz.assoc_in(config, k.split("."), v) if args.logdir is not None: config['basecall']['logdir'] = args.logdir try: cfg = voluptuous.Schema( { 'basecall': BasecallCfg.schema, 'version': str, }, extra=voluptuous.REMOVE_EXTRA, required=True)(config) logger.info(f"Parsed config\n{pformat(cfg)}") run(cfg['basecall']) except voluptuous.error.Error as e: logger.error(humanize_error(config, e)) sys.exit(1)
def run_args(args: argparse.Namespace): logger = hyperparam_logger with open(args.config) as f: config = yaml.load(f) for k, v in vars(args).items(): if v is not None and "." in k: config = toolz.assoc_in(config, k.split("."), v) print(k, v) try: cfg = voluptuous.Schema({ 'hyperparam': HyperParamCfg.schema, 'version': str, }, extra=voluptuous.REMOVE_EXTRA, required=True)(config) except voluptuous.error.Error as e: logger.error(humanize_error(config, e)) sys.exit(1) formatter = logging.Formatter( "%(asctime)s [%(levelname)5s]:%(name)20s: %(message)s" ) cfg: HyperParamCfg = cfg['hyperparam'] os.makedirs(cfg.work_dir, exist_ok=True) fn = os.path.join( cfg.work_dir, f"{getattr(args, 'name', 'mincall_hyper')}.log" ) h = (logging.FileHandler(fn)) h.setLevel(logging.DEBUG) h.setFormatter(formatter) hyperparam_logger.addHandler(h) logging.info(f"Added handler to {fn}") logger.info(f"Parsed config\n{pformat(cfg)}") run(cfg)
def run_args(args): config = {} for k, v in vars(args).items(): if v is not None and "." in k: config = toolz.assoc_in(config, k.split("."), v) try: print(config) cfg = voluptuous.Schema({"eval": EvalCfg.schema}, extra=voluptuous.REMOVE_EXTRA, required=True)(config) run(cfg['eval']) except voluptuous.error.Error as e: logger.error(humanize_error(config, e)) raise
def _pre_state(filler: Dict[str, Any]) -> Dict[str, Any]: test_name = get_test_name(filler) old_pre_state = filler[test_name].get("pre_state", {}) pre_state = normalize_state(raw_state) defaults = {address: { "balance": 0, "nonce": 0, "code": b"", "storage": {}, } for address in pre_state} new_pre_state = deep_merge(defaults, old_pre_state, pre_state) return assoc_in(filler, [test_name, "pre"], new_pre_state)
def state_definition_to_dict(state_definition: GeneralState) -> AccountState: """Convert a state definition to the canonical dict form. State can either be defined in the canonical form, or as a list of sub states that are then merged to one. Sub states can either be given as dictionaries themselves, or as tuples where the last element is the value and all others the keys for this value in the nested state dictionary. Example: ``` [ ("0xaabb", "balance", 3), ("0xaabb", "storage", { 4: 5, }), "0xbbcc", { "balance": 6, "nonce": 7 } ] ``` """ if isinstance(state_definition, Mapping): state_dict = state_definition elif isinstance(state_definition, Iterable): state_dicts = [ assoc_in( {}, state_item[:-1], state_item[-1] ) if not isinstance(state_item, Mapping) else state_item for state_item in state_definition ] if not is_cleanly_mergable(*state_dicts): raise ValidationError("Some state item is defined multiple times") state_dict = deep_merge(*state_dicts) else: assert TypeError("State definition must either be a mapping or a sequence") seen_keys = set(concat(d.keys() for d in state_dict.values())) bad_keys = seen_keys - set(["balance", "nonce", "storage", "code"]) if bad_keys: raise ValidationError( "State definition contains the following invalid account fields: {}".format( ", ".join(bad_keys) ) ) return state_dict
def parse_quinfig(self): # Parse all the arguments args = self.parse_args() override_args = dict( select_values(lambda v: v is not None, omit(args.__dict__, ['config']))) # Trick: first load the config without a schema quinfig = Quinfig(config_path=args.config) # Replace all the arguments passed into command line if len(override_args) > 0: print(f"Overriding arguments in {args.config} from command line.") for param, val in override_args.items(): param_path = param.split(".") old_val = tz.get_in(param_path, quinfig) print(f"> ({param}): {old_val} --> {val}") quinfig = tz.assoc_in(quinfig, param_path, val) # Load the config again, this time with the schema return Quinfig(config=quinfig.__dict__, schema=self.schema)
def replace_underscores(self, swept_parameter): """ Replace all the underscore references in sweep of swept_parameter. """ # Find all the references (i.e. dependencies) made by the swept_parameter references = [] for token in QuinSweep.SWEEP_TOKENS[: -1]: # omit default since it's value is never a dict if f"~{token}" in swept_parameter.sweep and is_mapping( swept_parameter.sweep[f"~{token}"]): references.extend( list(swept_parameter.sweep[f"~{token}"].keys())) # Find all the referred parameters parsed_references = list(map(QuinSweep.parse_ref_dotpath, references)) dotpaths = list(cat(parsed_references)) ref_dict = merge_with(compose(list, cat), *list(map(lambda e: dict([e]), dotpaths))) # TODO: there's a bug here potentially assert all(map(lambda l: len(l) == len(set(l)), list(itervalues(ref_dict)))), \ 'All conditions must be distinct.' ref_dict_no_underscores = walk_values( compose(set, autocurry(map)(int), autocurry(filter)(lambda e: e != '_')), ref_dict) if not references: return swept_parameter def compute_possibilities(full_dotpath, reference): # Look up the parameter using the dotpath parameter = self.swept_parameters_dict[full_dotpath] # Use the reference to figure out how many possiblities exist for the underscore if len(reference) > 0: # Merge all the sweeps performed for this parameter merged_sweep = merge( *list(filter(is_mapping, itervalues(parameter.sweep)))) # Look up the reference return len(merged_sweep[reference]) assert len( parameter.sweep ) == 1, 'If no reference, must be a single unconditional sweep.' # The number of possibilities is simply the number of values specified # in the (product/disjoint) unconditional sweep return len(list(parameter.sweep.values())[0]) # Update the sweep by replacing underscores updated_sweep = swept_parameter.sweep # Loop over all the parsed references for parsed_ref in parsed_references: # Expand all the partial dotpaths # TODO: remove? expanding all the partial dotpaths in the beginning? parsed_ref = list( map(lambda t: (self.expand_partial_dotpath(t[0]), t[1]), parsed_ref)) # For each parsed reference, there will be multiple (dotpath, idx) pairs for i, (full_dotpath, ref_idx) in enumerate(parsed_ref): # If the reference index is not an underscore, continue if not ref_idx == '_': continue # Compute the prefix reference prefix_reference = ".".join(list(cat(parsed_ref[:i]))) # Compute the number of possible ways to replace the underscore n_possibilities = compute_possibilities( full_dotpath, prefix_reference) replacements = set(range( n_possibilities)) - ref_dict_no_underscores[full_dotpath] # Find the path to the underscore condition path_to_condition = get_only_paths( updated_sweep, lambda p: any(lambda e: '_' in e, p), stop_at=full_dotpath)[0] # Find the value of the underscore condition value = tz.get_in(path_to_condition, updated_sweep) # Construct keys that are subtitutes for the underscore keys = list(map(lambda s: f'{full_dotpath}.{s}', replacements)) keys = list(map(lambda k: path_to_condition[:-1] + [k], keys)) # Update by adding those keys in for k in keys: updated_sweep = tz.assoc_in(updated_sweep, k, value) # Create a new swept parameter with the updated sweep swept_parameter = SweptParameter( swept_parameter.path, walk_values( iffy(is_mapping, autocurry(select_keys)(lambda k: '_' not in k)), updated_sweep)) return swept_parameter
def __init__(self, sweep_config_path=None, schema_path=None, sweep_config=None, schema=None): # Load up the sweep config if sweep_config is None: assert sweep_config_path is not None, 'Please pass in either sweep_config or sweep_config_path.' assert sweep_config_path.endswith( '.yaml'), 'Must use a YAML file for the sweep_config.' sweep_config = yaml.load(open(sweep_config_path), Loader=yaml.FullLoader) # First, extract the locations of the sweeps being performed self.sweep_paths = QuinSweep.parse_sweep_config(sweep_config) if len(self.sweep_paths) == 0: # No sweep: just return the single Quinfig self.quinfigs = [Quinfig(config=sweep_config)] print(f"Generated {len(self.quinfigs)} quinfig(s) successfully.") return # Create list of paths to all the parameters that are being swept self.swept_parameter_paths = list( distinct(map(QuinSweep.get_parameter_path, self.sweep_paths), key=tuple)) # Next, extract the fixed parameters from the sweep config self.fixed_parameters = QuinSweep.parse_fixed_parameters(sweep_config) # Next, fetch the SweptParameter named tuples after creating them self.swept_parameters, \ self.swept_disjoint_parameters, \ self.swept_product_parameters, \ self.swept_default_parameters = \ self.fetch_swept_parameters(sweep_config, self.sweep_paths, self.swept_parameter_paths) # For convenience, create a lookup table for the swept parameters from their dotpaths self.swept_parameters_dict = dict( zip(map(lambda sp: ".".join(sp.path), self.swept_parameters), self.swept_parameters)) # Expand all the dotpaths in any conditional self.expand_all_condition_dotpaths() # Filter out the unconditional sweeps and then process them uncond_disjoint_sweeps = list( filter(compose(is_seq, 1), self.swept_disjoint_parameters)) uncond_product_sweeps = list( filter(compose(is_seq, 1), self.swept_product_parameters)) self.all_combinations = self.process_unconditional_sweeps( uncond_disjoint_sweeps, uncond_product_sweeps) # Filter out the conditional sweeps and then process them cond_disjoint_sweeps = list( filter(compose(is_mapping, 1), self.swept_disjoint_parameters)) cond_product_sweeps = list( filter(compose(is_mapping, 1), self.swept_product_parameters)) self.process_conditional_sweeps(cond_disjoint_sweeps, cond_product_sweeps) # Generate all the Quinfigs self.quinfigs = [] for combination in self.all_combinations: coll = deepcopy(sweep_config) for parameter in combination: coll = tz.assoc_in(coll, parameter.path, parameter.value) self.quinfigs.append( Quinfig(config=coll, base_path=os.path.dirname( os.path.abspath(sweep_config_path)))) print(f"Generated {len(self.quinfigs)} quinfig(s) successfully.")
def unflatten_params(params: Dict[str, Any]): sol = {} for k, v in params.items(): sol = toolz.assoc_in(sol, k.split("."), v) return sol