示例#1
0
def call(name, args, R, library, is_function=True):
    C, O = R.context, R.options
    # Function call:
    _name = normalize_var(name)
    s = args and args.value.items() or []
    _args = [v for n, v in s if isinstance(n, int)]
    _kwargs = dict((str(n[1:]).replace('-', '_'), v) for n, v in s if not isinstance(n, int) and n != '_')
    _fn_a = '%s:%d' % (_name, len(_args))

    try:
        fn = O and O.get('@function ' + _fn_a)
        if fn:
            node = fn(R, *_args, **_kwargs)
        else:
            fn = library.lookup(_name, len(_args))
            node = fn(*_args, **_kwargs)
    except KeyError:
        sp = args and args.value.get('_') or ''
        if is_function:
            if not _css_functions_re.match(_name):
                log.error("Required function not found: %s (%s)", _fn_a, R.file_and_line, extra={'stack': True})
            _args = (sp + ' ').join(to_str(v) for n, v in s if isinstance(n, int))
            _kwargs = (sp + ' ').join('%s: %s' % (n, to_str(v)) for n, v in s if not isinstance(n, int) and n != '_')
            if _args and _kwargs:
                _args += (sp + ' ')
            # Function not found, simply write it as a string:
            node = StringValue(name + '(' + _args + _kwargs + ')')
        else:
            node = StringValue((sp + ' ').join(str(v) for n, v in s if n != '_'))
    return node
示例#2
0
def interpolate(var, rule, library):
    value = rule.context.get(normalize_var(var), var)
    if var != value and isinstance(value, basestring):
        _vi = eval_expr(value, rule, library, True)
        if _vi is not None:
            value = _vi
    return value
示例#3
0
    def evaluate(self, calculator, divide=False):
        # TODO bake this into the context and options "dicts", plus library
        func_name = normalize_var(self.func_name)

        argspec_node = self.argspec

        # Turn the pairs of arg tuples into *args and **kwargs
        # TODO unclear whether this is correct -- how does arg, kwarg, arg
        # work?
        args, kwargs = argspec_node.evaluate_call_args(calculator)
        argspec_len = len(args) + len(kwargs)

        # Translate variable names to Python identifiers
        # TODO what about duplicate kw names?  should this happen in argspec?
        # how does that affect mixins?
        kwargs = dict((key.lstrip('$').replace('-', '_'), value)
                      for key, value in kwargs.items())

        # TODO merge this with the library
        funct = None
        try:
            funct = calculator.namespace.function(func_name, argspec_len)
            # @functions take a ns as first arg.  TODO: Python functions possibly
            # should too
            if getattr(funct, '__name__', None) == '__call':
                funct = partial(funct, calculator.namespace)
        except KeyError:
            try:
                # DEVIATION: Fall back to single parameter
                funct = calculator.namespace.function(func_name, 1)
                args = [List(args, use_comma=True)]
            except KeyError:
                if not is_builtin_css_function(func_name):
                    log.error("Function not found: %s:%s",
                              func_name,
                              argspec_len,
                              extra={'stack': True})

        if funct:
            ret = funct(*args, **kwargs)
            if not isinstance(ret, Value):
                raise TypeError("Expected Sass type as return value, got %r" %
                                (ret, ))
            return ret

        # No matching function found, so render the computed values as a CSS
        # function call.  Slurpy arguments are expanded and named arguments are
        # unsupported.
        if kwargs:
            raise TypeError(
                "The CSS function %s doesn't support keyword arguments." %
                (func_name, ))

        # TODO another candidate for a "function call" sass type
        rendered_args = [arg.render() for arg in args]

        return String(u"%s(%s)" % (func_name, u", ".join(rendered_args)),
                      quotes=None)
示例#4
0
文件: ast.py 项目: 4teamwork/pyScss
    def evaluate(self, calculator, divide=False):
        # TODO bake this into the context and options "dicts", plus library
        func_name = normalize_var(self.func_name)

        argspec_node = self.argspec

        # Turn the pairs of arg tuples into *args and **kwargs
        # TODO unclear whether this is correct -- how does arg, kwarg, arg
        # work?
        args, kwargs = argspec_node.evaluate_call_args(calculator)
        argspec_len = len(args) + len(kwargs)

        # Translate variable names to Python identifiers
        # TODO what about duplicate kw names?  should this happen in argspec?
        # how does that affect mixins?
        kwargs = dict(
            (key.lstrip('$').replace('-', '_'), value)
            for key, value in kwargs.items())

        # TODO merge this with the library
        funct = None
        try:
            funct = calculator.namespace.function(func_name, argspec_len)
        except KeyError:
            try:
                # DEVIATION: Fall back to single parameter
                funct = calculator.namespace.function(func_name, 1)
                args = [List(args, use_comma=True)]
            except KeyError:
                if not is_builtin_css_function(func_name):
                    log.error("Function not found: %s:%s", func_name, argspec_len, extra={'stack': True})

        if funct:
            if getattr(funct, '_pyscss_needs_namespace', False):
                # @functions and some Python functions take the namespace as an
                # extra first argument
                ret = funct(calculator.namespace, *args, **kwargs)
            else:
                ret = funct(*args, **kwargs)
            if not isinstance(ret, Value):
                raise TypeError("Expected Sass type as return value, got %r" % (ret,))
            return ret

        # No matching function found, so render the computed values as a CSS
        # function call.  Slurpy arguments are expanded and named arguments are
        # unsupported.
        if kwargs:
            raise TypeError("The CSS function %s doesn't support keyword arguments." % (func_name,))

        # TODO another candidate for a "function call" sass type
        rendered_args = [arg.render() for arg in args]

        return String(
            "%s(%s)" % (func_name, ", ".join(rendered_args)),
            quotes=None)
示例#5
0
    def evaluate(self, calculator, divide=False):
        # TODO bake this into the context and options "dicts", plus library
        func_name = normalize_var(self.func_name)

        # Turn the pairs of arg tuples into *args and **kwargs
        # TODO unclear whether this is correct -- how does arg, kwarg, arg
        # work?
        args = []
        kwargs = {}
        evald_argpairs = []
        for var, expr in self.argspec.argpairs:
            value = expr.evaluate(calculator, divide=True)
            evald_argpairs.append((var, value))

            if var is None:
                args.append(value)
            else:
                kwargs[var.lstrip('$').replace('-', '_')] = value

        num_args = len(self.argspec.argpairs)

        # TODO merge this with the library
        try:
            func = calculator.namespace.function(func_name, num_args)
            # @functions take a ns as first arg.  TODO: Python functions possibly
            # should too
            if getattr(func, '__name__', None) == '__call':
                func = partial(func, calculator.namespace)
        except KeyError:
            if not is_builtin_css_function(func_name):
                log.error("Function not found: %s:%s",
                          func_name,
                          num_args,
                          extra={'stack': True})

            rendered_args = []
            for var, value in evald_argpairs:
                rendered_value = value.render()
                if var is None:
                    rendered_args.append(rendered_value)
                else:
                    rendered_args.append("%s: %s" % (var, rendered_value))

            return String(u"%s(%s)" % (func_name, u", ".join(rendered_args)),
                          quotes=None)
        else:
            return func(*args, **kwargs)
示例#6
0
    def evaluate(self, calculator, divide=False):
        # TODO bake this into the context and options "dicts", plus library
        name = normalize_var(self.func_name)

        # Turn the pairs of arg tuples into *args and **kwargs
        # TODO unclear whether this is correct -- how does arg, kwarg, arg
        # work?
        args = []
        kwargs = {}
        evald_argpairs = []
        for var, expr in self.argspec.argpairs:
            value = expr.evaluate(calculator, divide=True)
            evald_argpairs.append((var, value))

            if var is None:
                args.append(value)
            else:
                kwargs[ var.lstrip('$').replace('-', '_') ] = value

        num_args = len(self.argspec.argpairs)

        # TODO merge this with the library
        try:
            func = calculator.namespace.function(self.func_name, num_args)
            # @functions take a ns as first arg.  TODO: Python functions possibly
            # should too
            if getattr(func, '__name__', None) == '__call':
                func = partial(func, calculator.namespace)
        except KeyError:
            if not is_builtin_css_function(self.func_name):
                # TODO log.warn, log.error, warning, exception?
                log.warn("no such function")

            rendered_args = []
            for var, value in evald_argpairs:
                rendered_value = value.render()
                if var is None:
                    rendered_args.append(rendered_value)
                else:
                    rendered_args.append("%s: %s" % (var, rendered_value))

            return String(
                u"%s(%s)" % (self.func_name, u", ".join(rendered_args)),
                quotes=None)
        else:
            return func(*args, **kwargs)
示例#7
0
def eval_expr(expr, rule, library, raw=False):
    # print >>sys.stderr, '>>',expr,'<<'
    results = None

    if not isinstance(expr, basestring):
        results = expr

    if results is None:
        if _variable_re.match(expr):
            expr = normalize_var(expr)
        if expr in rule.context:
            chkd = {}
            while expr in rule.context and expr not in chkd:
                chkd[expr] = 1
                _expr = rule.context[expr]
                if _expr == expr:
                    break
                expr = _expr
        if not isinstance(expr, basestring):
            results = expr

    if results is None:
        if expr in expr_cache:
            results = expr_cache[expr]
        else:
            try:
                P = Calculator(CalculatorScanner(), library)
                P.reset(expr)
                results = P.goal(rule)
            except SyntaxError:
                if config.DEBUG:
                    raise
            except Exception, e:
                log.exception("Exception raised: %s in `%s' (%s)", e, expr, rule.file_and_line)
                if config.DEBUG:
                    raise

            # TODO this is a clumsy hack for nondeterministic functions;
            # something better (and per-compiler rather than global) would be
            # nice
            if '$' not in expr and '(' not in expr:
                expr_cache[expr] = results