Пример #1
0
    def testStacking(self):
        s = scope.ScopeStack({"x": 1}, {"x:": 2})
        s = scope.ScopeStack(s, {"x": 3})

        # Stack remains flat.
        self.assertEqual(s.locals, {"x": 3})
        self.assertEqual(s.globals, {"x": 1})
Пример #2
0
def infer(query,
          replacements=None,
          root_type=None,
          libs=("stdcore", "stdmath")):
    """Determine the type of the query's output without actually running it.

    Arguments:
        query: A query object or string with the query.
        replacements: Built-time parameters to the query, either as dict or as
            an array (for positional interpolation).
        root_type: The types of variables to be supplied to the query inference.
        libs: What standard libraries should be taken into account for the
            inference.

    Returns:
        The type of the query's output, if it can be determined. If undecidable,
        returns efilter.protocol.AnyType.

        NOTE: The inference returns the type of a row in the results, not of the
        actual Python object returned by 'apply'. For example, if a query
        returns multiple rows, each one of which is an integer, the type of the
        output is considered to be int, not a collection of rows.

    Examples:
        infer("5 + 5") # -> INumber

        infer("SELECT * FROM people WHERE age > 10") # -> AnyType

        # If root_type implements the IStructured reflection API:
        infer("SELECT * FROM people WHERE age > 10", root_type=...) # -> dict
    """
    # Always make the scope stack start with stdcore.
    if root_type:
        type_scope = scope.ScopeStack(std_core.MODULE, root_type)
    else:
        type_scope = scope.ScopeStack(std_core.MODULE)

    stdcore_included = False
    for lib in libs:
        if lib == "stdcore":
            stdcore_included = True
            continue

        module = std_core.LibraryModule.ALL_MODULES.get(lib)
        if not module:
            raise TypeError("No standard library module %r." % lib)

        type_scope = scope.ScopeStack(module, type_scope)

    if not stdcore_included:
        raise TypeError("'stdcore' must always be included.")

    query = q.Query(query, params=replacements)
    return infer_type.infer_type(query, type_scope)
Пример #3
0
def infer_type(query, scope=None):
    # Always include stdcore at the top level.
    if scope:
        scope = s.ScopeStack(std_core.MODULE, scope)
    else:
        scope = s.ScopeStack(std_core.MODULE)

    try:
        return infer_type(query.root, scope)
    except errors.EfilterError as error:
        error.query = query.source
        raise
Пример #4
0
def solve_bind(expr, vars):
    """Build a RowTuple from key/value pairs under the bind.

    The Bind subtree is arranged as follows:

    Bind
    | First KV Pair
    | | First Key Expression
    | | First Value Expression
    | Second KV Pair
    | | Second Key Expression
    | | Second Value Expression
    Etc...
    """
    local_scope = vars
    values = []
    keys = []
    for pair in expr.children:
        key = solve(pair.key, local_scope).value
        keys.append(key)
        value = solve(pair.value, local_scope).value
        values.append(value)
        local_scope = scope.ScopeStack(local_scope, {key: value})

    result = {}
    for k, v in zip(keys, values):
        result[k] = v

    return Result(result, ())
Пример #5
0
def solve_bind(expr, vars):
    """Build a RowTuple from key/value pairs under the bind.

    The Bind subtree is arranged as follows:

    Bind
    | First KV Pair
    | | First Key Expression
    | | First Value Expression
    | Second KV Pair
    | | Second Key Expression
    | | Second Value Expression
    Etc...

    As we evaluate the subtree, each subsequent KV pair is evaluated with
    the all previous bingings already in scope. For example:

    bind(x: 5, y: x + 5)  # Will bind y = 10 because x is already available.
    """
    value_expressions = []
    keys = []
    for pair in expr.children:
        keys.append(solve(pair.key, vars).value)
        value_expressions.append(pair.value)

    result = row_tuple.RowTuple(ordered_columns=keys)
    intermediate_scope = scope.ScopeStack(vars, result)

    for idx, value_expression in enumerate(value_expressions):
        value = solve(value_expression, intermediate_scope).value
        # Update the intermediate bindings so as to make earlier bindings
        # already available to the next child-expression.
        result[keys[idx]] = value

    return Result(result, ())
Пример #6
0
def solve_query(query, vars):
    # Standard library must always be included. Others are optional, and the
    # caller can add them to vars using ScopeStack.
    vars = scope.ScopeStack(std_core.MODULE, vars)
    try:
        return solve(query.root, vars)
    except errors.EfilterError as error:
        if not error.query:
            error.query = query.source
        raise
Пример #7
0
def __nest_scope(expr, outer, inner):
    try:
        return scope.ScopeStack(outer, inner)
    except TypeError:
        if protocol.implements(inner, applicative.IApplicative):
            raise errors.EfilterTypeError(
                root=expr, query=expr.source,
                message="Attempting to use a function %r as an object." % inner)

        raise errors.EfilterTypeError(
            root=expr, query=expr.source,
            message="Attempting to use %r as an object (IStructured)." % inner)
Пример #8
0
def infer_type(expr, scope):
    if not isinstance(scope, s.ScopeStack):
        scope = s.ScopeStack(scope)

    return scope.reflect(expr.value) or protocol.AnyType
Пример #9
0
def infer_type(expr, scope):
    t = infer_type(expr.context, scope)
    return infer_type(expr.expression, s.ScopeStack(scope, t))
Пример #10
0
def apply(query,
          replacements=None,
          vars=None,
          allow_io=False,
          libs=("stdcore", "stdmath")):
    """Run 'query' on 'vars' and return the result(s).

    Arguments:
        query: A query object or string with the query.
        replacements: Built-time parameters to the query, either as dict or
            as an array (for positional interpolation).
        vars: The variables to be supplied to the query solver.
        allow_io: (Default: False) Include 'stdio' and allow IO functions.
        libs: Iterable of library modules to include, given as strings.
            Default: ('stdcore', 'stdmath')
            For full list of bundled libraries, see efilter.stdlib.

            Note: 'stdcore' must always be included.

            WARNING: Including 'stdio' must be done in conjunction with
                'allow_io'. This is to make enabling IO explicit. 'allow_io'
                implies that 'stdio' should be included and so adding it to
                libs is actually not required.

    Notes on IO: If allow_io is set to True then 'stdio' will be included and
    the EFILTER query will be allowed to read files from disk. Use this with
    caution.

        If the query returns a lazily-evaluated result that depends on reading
        from a file (for example, filtering a CSV file) then the file
        descriptor will remain open until the returned result is deallocated.
        The caller is responsible for releasing the result when it's no longer
        needed.

    Returns:
        The result of evaluating the query. The type of the output will depend
        on the query, and can be predicted using 'infer' (provided reflection
        callbacks are implemented). In the common case of a SELECT query the
        return value will be an iterable of filtered data (actually an object
        implementing IRepeated, as well as __iter__.)

    A word on cardinality of the return value:
        Types in EFILTER always refer to a scalar. If apply returns more than
        one value, the type returned by 'infer' will refer to the type of
        the value inside the returned container.

        If you're unsure whether your query returns one or more values (rows),
        use the 'getvalues' function.

    Raises:
        efilter.errors.EfilterError if there are issues with the query.

    Examples:
        apply("5 + 5") # -> 10

        apply("SELECT * FROM people WHERE age > 10",
              vars={"people":({"age": 10, "name": "Bob"},
                              {"age": 20, "name": "Alice"},
                              {"age": 30, "name": "Eve"}))

        # This will replace the question mark (?) with the string "Bob" in a
        # safe manner, preventing SQL injection.
        apply("SELECT * FROM people WHERE name = ?", replacements=["Bob"], ...)
    """
    if vars is None:
        vars = {}

    if allow_io:
        libs = list(libs)
        libs.append("stdio")

    query = q.Query(query, params=replacements)

    stdcore_included = False
    for lib in libs:
        if lib == "stdcore":
            stdcore_included = True
            # 'solve' always includes this automatically - we don't have a say
            # in the matter.
            continue

        if lib == "stdio" and not allow_io:
            raise ValueError("Attempting to include 'stdio' but IO not "
                             "enabled. Pass allow_io=True.")

        module = std_core.LibraryModule.ALL_MODULES.get(lib)
        if not lib:
            raise ValueError("There is no standard library module %r." % lib)
        vars = scope.ScopeStack(module, vars)

    if not stdcore_included:
        raise ValueError("EFILTER cannot work without standard lib 'stdcore'.")

    results = solve.solve(query, vars).value

    return results
Пример #11
0
 def testResolutionOrder(self):
     s = scope.ScopeStack({"x": 1}, {"x:": 2}, {"x": 3})
     self.assertEqual(s.locals, {"x": 3})
     self.assertEqual(s.globals, {"x": 1})
     self.assertEqual(s.resolve("x"), 3)