def test_block_properties(): block = Block( Comment("comment", False, False), Block(String("hello"), String("There!")), ) block.append(Property(["foo", "bar"], Expression())) assert block.has_properties() is True assert block.has_media() is False
def test_block_media(): block = Block( Comment("comment", False, False), Block(String("hello"), String("There!")), ) block.append(Media("fizz")) assert block.has_properties() is False assert block.has_media() is True
def test_block_append(): block = Block( Comment("comment", False, False), Block(String("hello"), String("There!")), ) block.append(Literal("Literal")) block.append(true) assert block.nodes == [Literal("Literal"), true] assert block.has_properties() is False assert block.has_media() is False assert block.is_empty() is False
def current_media(*args, evaluator=None): def look_for_media(node): if node.node_name == "media": node.value = evaluator.visit(node.value) return str(node) else: block = node.block if block and block.parent and hasattr(block.parent, "node"): return look_for_media(block.parent.node) return None media = look_for_media(evaluator.closest_block().node) if media: return String(media) else: return String("")
def property_expression(self, prop, name): expr = Expression(lineno=self.parser.lineno, column=self.parser.column) value = prop.expr.clone(None) # name expr.append( String(prop.name, lineno=self.parser.lineno, column=self.parser.column)) # replace cyclic call with __CALL__ def replace(node): if (node.node_name == "call" and hasattr(node, "function_name") and name == node.function_name): return Literal( "__CALL__", lineno=self.parser.lineno, column=self.parser.column, ) if hasattr(node, "nodes") and node.nodes: node.nodes = [replace(n) for n in node.nodes] return node replace(value) expr.append(value) return expr
def test_lexer_keyword_string(): lexer = Lexer('if "fizz":\n return foo;\n', {}) tokens = [token for token in lexer] assert tokens[0] == Token("if", "if", lineno=1, column=1) assert tokens[1] == Token( "string", String("fizz", '"'), lineno=1, column=4 ) assert tokens[3] == Token("return", "return", lineno=1, column=11)
def test_lexer_urlchars_important(): lexer = Lexer('url("/images/foo.png")\n' "!important foo", {}) tokens = [token for token in lexer] assert tokens[1] == Token( "string", String("/images/foo.png", '"'), lineno=1, column=5 ) assert tokens[4] == Token( "ident", Literal("!important"), lineno=2, column=1 )
def replace(pattern, replacement, value, evaluator=None): assert_string(pattern, "pattern") assert_string(replacement, "replacement") assert_string(value, "value") result = value.string.replace(pattern.string, replacement.string) if isinstance(value, Ident): return Ident(result) else: return String(result)
def test_string(): assert String("hello", "there").node_name == "string" assert str(String("hello", null)) == "'hello'" assert str(String("hello", "there")) == "therehellothere" assert str(String("hello")) == "'hello'" assert String("hello").to_boolean() == true assert String("z").to_boolean() == Boolean(True) assert String("").to_boolean() == false
def lightness(color: Color, value=None, evaluator=None): if value: hsla_color = color.hsla() return hsla( Unit(hsla_color.hue), Unit(hsla_color.saturation), value, Unit(hsla_color.a), evaluator, ) return component(color, String("lightness"), evaluator)
def substr(value, start, length=None, evaluator=None): assert_string(value, "val") assert_type(start, "unit", "start") start = int(start.value) if length: length = start + int(length.value) result = value.string[start:length] if isinstance(value, Ident): return Ident(result) else: return String(result)
def hue(color: Color, value=None, evaluator=None): if value: hsla_color = color.hsla() return hsla( value, Unit(hsla_color.saturation), Unit(hsla_color.lightness), Unit(hsla_color.a), evaluator, ) return component(color, String("hue"))
def split(delim, value, evaluator=None): assert_string(delim, "delimiter") assert_string(value, "val") words = value.string.split(delim.string) expr = Expression() for word in words: if isinstance(value, Ident): addition = Ident(word) else: addition = String(word) expr.append(Ident(addition)) return expr
def setup(self): self.populate_global_scope() for file in reversed(self.imports): expr = Expression() expr.append( String(file, lineno=self.parser.lineno, column=self.parser.column)) self.root.nodes.insert( 0, Import(expr, lineno=self.parser.lineno, column=self.parser.column), )
def selectors(evaluator=None): stack = evaluator.selector_stack() expr = Expression(is_list=True) if stack: for i, group in enumerate(stack): if len(group) > 1: selector_values = [] for selector in group: nested = SelectorParser(selector.value).parse()["nested"] if nested and i != 0: selector_values.append(f"& {selector.value}") else: selector_values.append(f"{selector.value}") expr.append(String(",".join(selector_values))) else: selector = group[0].value nested = SelectorParser(selector).parse()["nested"] if nested and i != 0: expr.append(String(f"& {selector}")) else: expr.append(String(f"{selector}")) else: expr.append(String("&")) return expr
def visit_each(self, each): self.result += 1 expr = utils.unwrap(self.visit(each.expr)) length = len(expr.nodes) val = Ident(each.value) key = Ident("__index__") if each.key: key = Ident(each.key) scope = self.get_current_scope() block = self.get_current_block() vals = [] self.result -= 1 each.block.scope = False def visit_body(key, value): scope.add(value) scope.add(key) body = self.visit(each.block.clone()) vals.extend(body.nodes) # for prop in obj if length == 1 and "objectnode" == expr.nodes[0].node_name: obj = expr.nodes[0] for prop in obj.values: val.value = String(prop, lineno=self.parser.lineno, column=self.parser.column) key.value = obj.get(prop) # checkme: works? visit_body(key, val) else: for i, n in enumerate(expr.nodes): val.value = n key.value = Unit(i) visit_body(key, val) self.mixin(vals, block) if vals and len(vals) > 0: return vals[len(vals) - 1] else: return null
def test_lexer_atrules(): lexer = Lexer( "@viewport {\n" " color: blue\n" "}\n" "\n" '@namespace svg "http://www.w3.org/2000/svg"\n' "@-moz-viewport\n", {}, ) tokens = [token for token in lexer] assert tokens[0] == Token("atrule", "viewport", lineno=1, column=1) assert tokens[9] == Token("namespace", lineno=5, column=1) assert tokens[12] == Token( "string", String("http://www.w3.org/2000/svg", '"'), lineno=5, column=16, ) assert tokens[14] == Token("atrule", "-moz-viewport", lineno=6, column=1)
def slice(values, start, end=None, evaluator=None): start = int(start.nodes[0].value) if end and len(end.nodes) > 0: end = int(end.nodes[0].value) values = utils.unwrap(values).nodes if len(values) > 1: if end: return utils.coerce(values[start:end], raw=True) else: return utils.coerce(values[start:], raw=True) if end: result = values[0].string[start:end] else: result = values[0].string[start:] if isinstance(values[0], Ident): return Ident(result) else: return String(result)
def test_component_rgba_lightness(): color = RGBA(100, 100, 50, 0.5) string = String("blue") unit = component(color, string) assert unit == Unit(50, "%")
def test_component_rgba_saturation(): color = RGBA(100, 100, 100, 0.5) string = String("green") unit = component(color, string) assert unit == Unit(100, "%")
def test_component_rgba_hue(): color = RGBA(200, 100, 50, 0.5) string = String("red") unit = component(color, string) assert unit == Unit(200, "deg")
def test_component_hsla_lightness(): color = HSLA(100, 100, 50, 0.5) string = String("lightness") unit = component(color, string) assert unit == Unit(50, "%")
def test_component_hsla_saturation(): color = HSLA(100, 100, 100, 0.5) string = String("saturation") unit = component(color, string) assert unit == Unit(100, "%")
def test_component_hsla_hue(): color = HSLA(200, 100, 50, 0.5) string = String("hue") unit = component(color, string) assert unit == Unit(200, "deg")
def test_coerce(): first = String("hello") other = String("there") assert first.coerce(other) == other expression = Expression() expression.append(String("one")) expression.append(String("two")) expression.append(String("three")) assert first.coerce(expression) == String("one two three") assert first.coerce(null) == String("null") assert first.coerce(Ident("foobar")) == String("foobar")
def list_separator(list, evaluator=None): list = unwrap(list) return String("," if list.is_list else " ")
def test_match(): result = match(String("^(height|width)?([<>=]{1,})(.*)"), String("height>=1024px")) assert result == ["height>=1024px", "height", ">=", "1024px"] result = match(String("^foo(?:bar)?"), String("foo")) assert result == ["foo"] result = match(String("^foo(?:bar)?"), String("foobar")) assert result == ["foobar"] result = match(String("^foo(?:bar)?"), String("bar")) assert result == null result = match(String("ain"), String("The rain in SPAIN stays mainly in the plain")) assert result == ["ain"] result = match( String("ain"), String("The rain in SPAIN stays mainly in the plain"), String("g"), ) assert result == ["ain", "ain", "ain"] result = match( String("ain"), String("The rain in SPAIN stays mainly in the plain"), String("ig"), ) assert result == ["ain", "AIN", "ain", "ain"]
def invoke_function(self, fn, args, content): block = Block( fn.block.parent, None, lineno=self.parser.lineno, column=self.parser.column, ) # clone the function body to prevent mutation of subsequent calls body = fn.block.clone(block) mixin_block = self.stack.current_frame().block # new block scope self.stack.append(Frame(block)) scope = self.get_current_scope() # normalize arguments if args.node_name != "arguments": expr = Expression() expr.append(args) args = Arguments.from_expression(expr) # arguments scope.add(Ident("arguments", args)) # mixin scope introspection bn = mixin_block.node_name if self.result: scope.add(Ident("mixin", false)) else: scope.add( Ident( "mixin", String( bn, lineno=self.parser.lineno, column=self.parser.column, ), )) # current property if self.property: prop = self.property_expression(self.property, fn.function_name) scope.add( Ident( "current-property", prop, lineno=self.parser.lineno, column=self.parser.column, )) else: scope.add( Ident( "current-property", null, lineno=self.parser.lineno, column=self.parser.column, )) # current call stack expr = Expression(lineno=self.parser.lineno, column=self.parser.column) for call in reversed(self.calling[:-1]): expr.append( Literal(call, lineno=self.parser.lineno, column=self.parser.column)) scope.add( Ident( "called-from", expr, lineno=self.parser.lineno, column=self.parser.column, )) # inject arguments as locals # todo: rewrite this! i = 0 for index, node in enumerate(fn.params.nodes): # rest param support if node.rest: node.value = Expression(lineno=self.parser.lineno, column=self.parser.column) for n in args.nodes[i:]: node.value.append(n) node.value.preserve = True node.value.is_list = args.is_list else: # argument default support # in dict? arg = args.map.get(node.name) # next node? if not arg: if hasattr(args, "nodes") and i < len(args.nodes): arg = args.nodes[i] i += 1 node = node.clone() if arg is not None: if hasattr(arg, "is_empty") and arg.is_empty(): args.nodes[i - 1] = self.visit(node) else: node.value = arg else: args.append(node.value) # required argument not satisfied if not node.value: raise TypeError(f'argument "{node}" required for {fn}') scope.add(node) # mixin block if content: scope.add(Ident("block", content, True)) # invoke return self.invoke(body, True, fn.filename)