Exemple #1
0
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)
Exemple #2
0
    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)
Exemple #3
0
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
Exemple #4
0
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)
Exemple #5
0
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)
Exemple #6
0
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
Exemple #7
0
    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)
Exemple #8
0
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
Exemple #9
0
    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)
Exemple #10
0
    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
Exemple #11
0
    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.")
Exemple #12
0
def unflatten_params(params: Dict[str, Any]):
    sol = {}
    for k, v in params.items():
        sol = toolz.assoc_in(sol, k.split("."), v)
    return sol