예제 #1
0
    def get_function(self, funcname):
        """Given a function name, return it. Exit on error if not found.
        """
        # Import the function
        sys.path.insert(0, os.path.dirname(self.filename))
        module = ".".join(funcname.split(".")[:-1])
        funcname = funcname.split(".")[-1]
        try:
            module = import_module(module)
            func = getattr(module, funcname)
            if func is None:
                bot.exit(f"Cannot find {funcname}.")
        except:
            bot.exit(f"Cannot import grid function {funcname}")

        return func
예제 #2
0
def test_basic(
    funcname,
    module,
    filename,
    func=None,
    args=None,
    returns=None,
    interactive=False,
    metrics=None,
):
    """test basic is a worker version of the task.test_basic function.
       If a function is not provided, funcname, module, and filename are
       required to retrieve it. A function can only be provided directly
       if it is pickle serializable (multiprocessing would require this).
       It works equivalently but is not attached to a class, and returns
       a list of values for [passed, result, out, err, raises]

       Arguments:
         - funcname (str) : the name of the function to import
         - module (str) : the base module to get the function from
         - func (Function) : if running serial, function can be directly provided
         - args (dict) : dictionary of arguments
         - returns (type) : a returns type to test for
         - interactive (bool) : run in interactive mode (giving user shell)
         - metrics (list) : one or more metrics (decorators) to run.
    """
    metrics = metrics or []

    if not func:
        sys.path.insert(0, os.path.dirname(filename))
        func = get_function(
            module=module, funcname=funcname, args=args, filename=filename
        )

    # Figure out how to apply multiple
    originalfunc = func
    passed = False
    result = None
    raises = None
    out = []
    err = []

    # import the decorators here (currently only support decorators from gridtest
    for metric in metrics:
        if not metric.startswith("@"):
            continue
        metric = re.sub("^[@]", "", metric)
        try:
            gt = import_module("gridtest.decorators")
            decorator = getattr(gt, metric)

            # Update func to include wrapper
            func = decorator(func)

        # Fallback to support for custom modules
        except:
            try:
                metric_module = metric.split(".")[0]
                mm = import_module(metric_module)
                for piece in metric.split(".")[1:]:
                    decorator = getattr(mm, piece)

                # Update func to include wrapper
                func = decorator(func)
            except:
                out.append(f"Warning, unable to import decorator @{metric}")

    # Interactive mode means giving the user console control
    if interactive:
        print_interactive(**locals())
        try:
            import IPython

            IPython.embed()
        except:
            import code

            code.interact(local=locals())

    if not func:
        err = [f"Cannot find function {funcname}"]

    else:
        # Subset arguments down to those allowed
        args = intersect_args(originalfunc, args)
        passed, error = test_types(originalfunc, args, returns)
        err += error

        # if type doesn't pass, TypeError, otherwise continue
        if not passed:
            raises = "TypeError"

        else:

            # Run and capture output and error
            try:
                with Capturing() as output:
                    result = func(**args)
                if output:
                    std = output.pop(0)
                    out += std.get("out")
                    err += std.get("err")
                passed = True
            except Exception as e:
                raises = type(e).__name__
                message = str(e)
                if message:
                    err.append(message)

    return [passed, result, out, err, raises]
예제 #3
0
def substitute_func(value, funcs=None):
    """Given a value, determine if it contains a function substitution,
       and if it's one an important function (e.g., one from gridtest.helpers)
       return the value with the function applied. 

       Arguments:
         - value (str) : the value to do the substitution for.
         - funcs (dict) : lookup dictionary of functions to be used

       Notes: 
         A function should be in the format: {% tempfile.mkdtemp %} 
         (global import) or a function in gridtest.func in the format 
         {% tmp_path %}. If arguments are supplied, they should be in 
         the format {% tmp_path arg1=1 arg2=2 %}
    """
    # Numbers cannot have replacement
    if not isinstance(value, str):
        return value

    # First do substitutions of variables
    for template in re.findall("{%.+%}", value):
        varname = re.sub("({%|%})", "", template)
        params = [x.strip() for x in varname.split(" ") if x]

        # Split module.name.func into module.name func
        modulename = params.pop(0).rsplit(".", 1)[0]
        funcpath = modulename[1:]
        func = None

        # Case 1: we have a known gridtest function
        if modulename in GRIDTEST_FUNCS:
            funcpath = modulename
            modulename = "gridtest.func"

        # Case 2: a function is supplied directly in the lookup
        elif funcs and modulename in funcs:
            func = funcs.get(modulename)

        # Case 3: Custom module provided by the user
        else:
            funcpath = funcpath[0]

        # The function path needs to be provided
        if not funcpath and not func:
            sys.exit(f"A function name must be provided for {varname}")

        # If used from within Python, the function might be supplied
        if not func:
            try:
                module = import_module(modulename)
                func = getattr(module, funcpath)
            except:
                sys.exit(f"Cannot import module {modulename}")

        # If function is found, get value
        if not func:
            sys.exit(
                f"Cannot import function {funcpath} from module {modulename}")

        kwargs = {}
        params = {x.split("=")[0]: x.split("=")[1] for x in params}

        # Clean up parameters based on intuited types
        for paramname, paramvalue in params.items():
            if paramvalue == "None":
                paramvalue = None

            # Booleans
            elif paramvalue == "True":
                paramvalue = True
            elif paramvalue == "False":
                paramvalue = False

            # No quotes and all numeric, probably int
            elif re.search("^[0-9]+$", paramvalue):
                paramvalue = int(paramvalue)

            # One decimal, all numbers, probably float
            elif re.search("^[0-9]+[.]([0-9]+)?$", paramvalue):
                paramvalue = float(paramvalue)

            # Explicitly a string with quotes
            elif re.search('^(".+")$', paramvalue):
                paramvalue = paramvalue.strip('"')
            elif re.search("^('.+')$", paramvalue):
                paramvalue = paramvalue.strip("'")
            kwargs[paramname] = paramvalue

        new_value = func(**kwargs)
        value = re.sub(template, value, new_value)

    return value