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
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
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)
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)
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)
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)
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