def test_addition(): # Numbers are a little complicated, what with all the units # Simple case assert Number(123) + Number(456) == Number(579) # Simple equal units assert Number(1, "px") + Number(2, "px") == Number(3, "px") # Unitless values inherit units of the other operand assert Number(5) + Number(6, "px") == Number(11, "px") # Zero values can cast to any units assert Number(0, "in") + Number(24, "deg") == Number(24, "deg") # With different units, the left operand wins assert Number(10, "cm") + Number(100, "mm") == Number(20, "cm") assert Number(100, "mm") + Number(10, "cm") == Number(200, "mm") # Unconvertible units raise an error with pytest.raises(ValueError): Number(1, "px") + Number(1, "em") # Adding anything to a string makes a string assert Number(123) + String('abc') == String('123abc') assert String('abc') + Number(123) == String('abc123') ret = String('abc', quotes=None) + String('def', quotes=None) assert ret == String('abcdef') assert ret.quotes is None ret = String('abc', quotes='"') + String('def', quotes=None) assert ret == String('abcdef') assert ret.quotes is '"' ret = String('abc', quotes=None) + String('def', quotes='"') assert ret == String('abcdef') assert ret.quotes is None assert Color.from_hex('#010305') + Color.from_hex('#050301') == Color.from_hex('#060606') assert Color.from_name('white') + Color.from_name('white') == Color.from_name('white')
def customise_css(): colour = colours.get_from_qs(flask.request.args) namespace = Namespace() namespace.set_variable('$fg', Color.from_hex(colour.fg)) namespace.set_variable('$bg', Color.from_hex(colour.bg)) namespace.set_variable('$highFg', Color.from_hex(colour.highFg)) namespace.set_variable('$highBg', Color.from_hex(colour.highBg)) compiler = Compiler(namespace=namespace) res = make_response(compiler.compile('style/index.scss')) res.mimetype = 'text/css' return res
def test_reference_operations(): """Test the example expressions in the reference document: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#operations """ # TODO: break this into its own file and add the entire reference guide # Need to build the calculator manually to get at its namespace, and need # to use calculate() instead of evaluate_expression() so interpolation # works ns = CoreExtension.namespace.derive() calc = Calculator(ns).calculate # Simple example assert calc('1in + 8pt') == Number(1.1111111111111112, "in") # Division ns.set_variable('$width', Number(1000, "px")) ns.set_variable('$font-size', Number(12, "px")) ns.set_variable('$line-height', Number(30, "px")) assert calc('10px/8px') == String('10px / 8px') # plain CSS; no division assert calc('$width/2') == Number(500, "px") # uses a variable; does division assert calc('(500px/2)') == Number(250, "px") # uses parens; does division assert calc('5px + 8px/2px') == Number(9, "px") # uses +; does division # TODO, again: Ruby Sass correctly renders this without spaces assert calc('#{$font-size}/#{$line-height}') == String('12px / 30px') # uses #{}; does no division # Modulo assert calc('29 % 12') == Number(5) assert calc('29px % 12') == Number(5, 'px') assert calc('29px % 12px') == Number(5, 'px') # Color operations ns.set_variable('$translucent-red', Color.from_rgb(1, 0, 0, 0.5)) ns.set_variable('$green', Color.from_name('lime')) assert calc('#010203 + #040506') == Color.from_hex('#050709') assert calc('#010203 * 2') == Color.from_hex('#020406') assert calc('rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75)') == Color.from_rgb(1, 1, 0, 0.75) assert calc('opacify($translucent-red, 0.3)') == Color.from_rgb(1, 0, 0, 0.8) assert calc('transparentize($translucent-red, 0.25)') == Color.from_rgb(1, 0, 0, 0.25) assert calc("progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}')" ).render() == "progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#FF00FF00', endColorstr='#80FF0000')" # String operations ns.set_variable('$value', Null()) assert_strict_string_eq(calc('e + -resize'), String('e-resize', quotes=None)) assert_strict_string_eq(calc('"Foo " + Bar'), String('Foo Bar', quotes='"')) assert_strict_string_eq(calc('sans- + "serif"'), String('sans-serif', quotes=None)) assert calc('3px + 4px auto') == List([Number(7, "px"), String('auto', quotes=None)]) assert_strict_string_eq(calc('"I ate #{5 + 10} pies!"'), String('I ate 15 pies!', quotes='"')) assert_strict_string_eq(calc('"I ate #{$value} pies!"'), String('I ate pies!', quotes='"'))
def kwatom(self): _token_ = self._peek(self.kwatom_rsts) if _token_ == '":"': pass elif _token_ == 'KWID': KWID = self._scan('KWID') return Literal(parse_bareword(KWID)) elif _token_ == 'KWNUM': KWNUM = self._scan('KWNUM') UNITS = None if self._peek(self.kwatom_rsts_) == 'UNITS': UNITS = self._scan('UNITS') return Literal(Number(float(KWNUM), unit=UNITS)) elif _token_ == 'KWSTR': KWSTR = self._scan('KWSTR') return Literal(String(dequote(KWSTR), quotes="'")) elif _token_ == 'KWQSTR': KWQSTR = self._scan('KWQSTR') return Literal(String(dequote(KWQSTR), quotes='"')) elif _token_ == 'KWCOLOR': KWCOLOR = self._scan('KWCOLOR') return Literal(Color.from_hex(KWCOLOR, literal=True)) else: # == 'KWVAR' KWVAR = self._scan('KWVAR') return Variable(KWVAR)
def test_reference_operations(): """Test the example expressions in the reference document: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#operations """ # TODO: break this into its own file and add the entire reference guide # Need to build the calculator manually to get at its namespace, and need # to use calculate() instead of evaluate_expression() so interpolation # works ns = Namespace(functions=CORE_LIBRARY) calc = Calculator(ns).calculate # Simple example assert calc('1in + 8pt') == Number(1.11111111, "in") # Division ns.set_variable('$width', Number(1000, "px")) ns.set_variable('$font-size', Number(12, "px")) ns.set_variable('$line-height', Number(30, "px")) assert calc('10px/8px') == String('10px / 8px') # plain CSS; no division assert calc('$width/2') == Number(500, "px") # uses a variable; does division assert calc('(500px/2)') == Number(250, "px") # uses parens; does division assert calc('5px + 8px/2px') == Number(9, "px") # uses +; does division # TODO: Ruby doesn't include these spaces assert calc('#{$font-size}/#{$line-height}') == String('12px / 30px') # uses #{}; does no division # Color operations ns.set_variable('$translucent-red', Color.from_rgb(1, 0, 0, 0.5)) ns.set_variable('$green', Color.from_name('lime')) assert calc('#010203 + #040506') == Color.from_hex('#050709') assert calc('#010203 * 2') == Color.from_hex('#020406') assert calc('rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75)') == Color.from_rgb(1, 1, 0, 0.75) assert calc('opacify($translucent-red, 0.3)') == Color.from_rgb(1, 0, 0, 0.9) assert calc('transparentize($translucent-red, 0.25)') == Color.from_rgb(1, 0, 0, 0.25) assert calc("progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}')" ) == "progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=#FF00FF00, endColorstr=#80FF0000)" # String operations ns.set_variable('$value', Null()) assert calc('e + -resize') == 'e-resize' assert calc('"Foo " + Bar') == '"Foo Bar"' assert calc('sans- + "serif"') == 'sans-serif' assert calc('3px + 4px auto') == '7px auto' assert calc('"I ate #{5 + 10} pies!"') == '"I ate 15 pies!"' assert calc('"I ate #{$value} pies!"') == '"I ate pies!"'
def test_addition(): # Numbers are a little complicated, what with all the units # Simple case assert Number(123) + Number(456) == Number(579) # Simple equal units assert Number(1, "px") + Number(2, "px") == Number(3, "px") # Unitless values inherit units of the other operand assert Number(5) + Number(6, "px") == Number(11, "px") # Zero values can cast to any units assert Number(0, "in") + Number(24, "deg") == Number(24, "deg") # With different units, the left operand wins assert Number(10, "cm") + Number(100, "mm") == Number(20, "cm") assert Number(100, "mm") + Number(10, "cm") == Number(200, "mm") # Unconvertible units raise an error with pytest.raises(ValueError): Number(1, "px") + Number(1, "em") # Adding anything to a string makes a string assert Number(123) + String('abc') == String('123abc') assert String('abc') + Number(123) == String('abc123') ret = String('abc', quotes=None) + String('def', quotes=None) assert ret == String('abcdef') assert ret.quotes is None ret = String('abc', quotes='"') + String('def', quotes=None) assert ret == String('abcdef') assert ret.quotes == '"' ret = String('abc', quotes=None) + String('def', quotes='"') assert ret == String('abcdef') assert ret.quotes is None assert Color.from_hex('#010305') + Color.from_hex( '#050301') == Color.from_hex('#060606') assert Color.from_name('white') + Color.from_name( 'white') == Color.from_name('white')
def atom(self): _token_ = self._peek(self.u_expr_chks) if _token_ == 'LPAR': LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts) if _token_ == 'RPAR': v = ListLiteral([], comma=False) elif _token_ not in self.argspec_item_chks: expr_map = self.expr_map() v = expr_map else: # in self.argspec_item_chks expr_lst = self.expr_lst() v = expr_lst RPAR = self._scan('RPAR') return Parentheses(v) elif _token_ == 'FNCT': FNCT = self._scan('FNCT') LPAR = self._scan('LPAR') argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp(FNCT, argspec) elif _token_ == 'BANG_IMPORTANT': BANG_IMPORTANT = self._scan('BANG_IMPORTANT') return Literal(String(BANG_IMPORTANT, quotes=None)) elif _token_ == 'ID': ID = self._scan('ID') return Literal(parse_bareword(ID)) elif _token_ == 'NUM': NUM = self._scan('NUM') UNITS = None if self._peek(self.atom_rsts_) == 'UNITS': UNITS = self._scan('UNITS') return Literal(Number(float(NUM), unit=UNITS)) elif _token_ == 'STR': STR = self._scan('STR') return Literal(String(STR[1:-1], quotes="'")) elif _token_ == 'QSTR': QSTR = self._scan('QSTR') return Literal(String(QSTR[1:-1], quotes='"')) elif _token_ == 'COLOR': COLOR = self._scan('COLOR') return Literal(Color.from_hex(COLOR, literal=True)) else: # == 'VAR' VAR = self._scan('VAR') return Variable(VAR)
def test_addition(calc): assert calc('123 + 456') == Number(579) assert calc('1px + 2px') == Number(3, "px") assert calc('123 + abc') == String('123abc') assert calc('abc + 123') == String('abc123') assert calc('abc + def') == String('abcdef') assert calc('abc + "def"') == String('abcdef') ret = calc('"abc" + def') assert ret == String('abcdef') assert ret.quotes == '"' ret = calc('"abc" + "def"') assert ret == String('abcdef') assert ret.quotes == '"' assert calc('#010305 + #050301') == Color.from_hex('#060606') assert calc('#ffffff + #ffffff') == Color.from_name('white')
def atom(self): _token_ = self._peek(self.u_expr_chks) if _token_ == 'LPAR': LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts) if _token_ not in self.argspec_item_chks: expr_map = self.expr_map() v = expr_map else: # in self.argspec_item_chks expr_lst = self.expr_lst() v = expr_lst RPAR = self._scan('RPAR') return Parentheses(v) elif _token_ == 'FNCT': FNCT = self._scan('FNCT') LPAR = self._scan('LPAR') argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp(FNCT, argspec) elif _token_ == 'BANG_IMPORTANT': BANG_IMPORTANT = self._scan('BANG_IMPORTANT') return Literal(String(BANG_IMPORTANT, quotes=None)) elif _token_ == 'ID': ID = self._scan('ID') return Literal(parse_bareword(ID)) elif _token_ == 'NUM': NUM = self._scan('NUM') UNITS = None if self._peek(self.atom_rsts_) == 'UNITS': UNITS = self._scan('UNITS') return Literal(Number(float(NUM), unit=UNITS)) elif _token_ == 'STR': STR = self._scan('STR') return Literal(String(STR[1:-1], quotes="'")) elif _token_ == 'QSTR': QSTR = self._scan('QSTR') return Literal(String(QSTR[1:-1], quotes='"')) elif _token_ == 'COLOR': COLOR = self._scan('COLOR') return Literal(Color.from_hex(COLOR, literal=True)) else: # == 'VAR' VAR = self._scan('VAR') return Variable(VAR)
def atom(self): _token_ = self._peek(self.u_expr_chks) if _token_ == 'LPAR': LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts) if _token_ == 'RPAR': v = ListLiteral([], comma=False) else: # in self.argspec_item_chks expr_map_or_list = self.expr_map_or_list() v = expr_map_or_list RPAR = self._scan('RPAR') return Parentheses(v) elif _token_ == 'URL_FUNCTION': URL_FUNCTION = self._scan('URL_FUNCTION') LPAR = self._scan('LPAR') interpolated_url = self.interpolated_url() RPAR = self._scan('RPAR') return interpolated_url elif _token_ == 'ALPHA_FUNCTION': ALPHA_FUNCTION = self._scan('ALPHA_FUNCTION') LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts_) if _token_ == 'OPACITY': OPACITY = self._scan('OPACITY') self._scan('"="') atom = self.atom() RPAR = self._scan('RPAR') return AlphaFunctionLiteral(atom) else: # in self.atom_chks argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp("alpha", argspec) elif _token_ == 'IF_FUNCTION': IF_FUNCTION = self._scan('IF_FUNCTION') LPAR = self._scan('LPAR') expr_lst = self.expr_lst() RPAR = self._scan('RPAR') return TernaryOp(expr_lst) elif _token_ == 'LITERAL_FUNCTION': LITERAL_FUNCTION = self._scan('LITERAL_FUNCTION') LPAR = self._scan('LPAR') interpolated_function = self.interpolated_function() RPAR = self._scan('RPAR') return Interpolation.maybe(interpolated_function, type=Function, function_name=LITERAL_FUNCTION) elif _token_ == 'FNCT': FNCT = self._scan('FNCT') LPAR = self._scan('LPAR') argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp(FNCT, argspec) elif _token_ == 'BANG_IMPORTANT': BANG_IMPORTANT = self._scan('BANG_IMPORTANT') return Literal(String.unquoted("!important", literal=True)) elif _token_ in self.atom_chks_: interpolated_bareword = self.interpolated_bareword() return Interpolation.maybe(interpolated_bareword) elif _token_ == 'NUM': NUM = self._scan('NUM') UNITS = None if self._peek(self.atom_rsts__) == 'UNITS': UNITS = self._scan('UNITS') return Literal(Number(float(NUM), unit=UNITS)) elif _token_ not in self.atom_chks__: interpolated_string = self.interpolated_string() return interpolated_string elif _token_ == 'COLOR': COLOR = self._scan('COLOR') return Literal(Color.from_hex(COLOR, literal=True)) else: # == 'VAR' VAR = self._scan('VAR') return Variable(VAR)
def atom(self): _token_ = self._peek(self.u_expr_chks) if _token_ == 'LPAR': LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts) if _token_ == 'RPAR': v = ListLiteral([], comma=False) else: # in self.argspec_item_chks expr_map_or_list = self.expr_map_or_list() v = expr_map_or_list RPAR = self._scan('RPAR') return Parentheses(v) elif _token_ == 'URL_FUNCTION': URL_FUNCTION = self._scan('URL_FUNCTION') LPAR = self._scan('LPAR') interpolated_url = self.interpolated_url() RPAR = self._scan('RPAR') return interpolated_url elif _token_ == 'ALPHA_FUNCTION': ALPHA_FUNCTION = self._scan('ALPHA_FUNCTION') LPAR = self._scan('LPAR') _token_ = self._peek(self.atom_rsts_) if _token_ == '"opacity"': self._scan('"opacity"') self._scan('"="') atom = self.atom() RPAR = self._scan('RPAR') return AlphaFunctionLiteral(atom) else: # in self.atom_chks argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp("alpha", argspec) elif _token_ == 'LITERAL_FUNCTION': LITERAL_FUNCTION = self._scan('LITERAL_FUNCTION') LPAR = self._scan('LPAR') interpolated_function = self.interpolated_function() RPAR = self._scan('RPAR') return Interpolation.maybe(interpolated_function, type=Function, function_name=LITERAL_FUNCTION) elif _token_ == 'FNCT': FNCT = self._scan('FNCT') LPAR = self._scan('LPAR') argspec = self.argspec() RPAR = self._scan('RPAR') return CallOp(FNCT, argspec) elif _token_ == 'BANG_IMPORTANT': BANG_IMPORTANT = self._scan('BANG_IMPORTANT') return Literal(String.unquoted("!important", literal=True)) elif _token_ in self.atom_chks_: interpolated_bareword = self.interpolated_bareword() return Interpolation.maybe(interpolated_bareword) elif _token_ == 'NUM': NUM = self._scan('NUM') UNITS = None if self._peek(self.atom_rsts__) == 'UNITS': UNITS = self._scan('UNITS') return Literal(Number(float(NUM), unit=UNITS)) elif _token_ not in self.atom_chks__: interpolated_string = self.interpolated_string() return interpolated_string elif _token_ == 'COLOR': COLOR = self._scan('COLOR') return Literal(Color.from_hex(COLOR, literal=True)) else: # == 'VAR' VAR = self._scan('VAR') return Variable(VAR)
def atom(self): _token_ = self._peek(self.u_expr_chks) if _token_ == "LPAR": LPAR = self._scan("LPAR") _token_ = self._peek(self.atom_rsts) if _token_ == "RPAR": v = ListLiteral([], comma=False) else: # in self.argspec_item_chks expr_map_or_list = self.expr_map_or_list() v = expr_map_or_list RPAR = self._scan("RPAR") return Parentheses(v) elif _token_ == "URL_FUNCTION": URL_FUNCTION = self._scan("URL_FUNCTION") LPAR = self._scan("LPAR") interpolated_url = self.interpolated_url() RPAR = self._scan("RPAR") return interpolated_url elif _token_ == "ALPHA_FUNCTION": ALPHA_FUNCTION = self._scan("ALPHA_FUNCTION") LPAR = self._scan("LPAR") _token_ = self._peek(self.atom_rsts_) if _token_ == '"opacity"': self._scan('"opacity"') self._scan('"="') NUM = self._scan("NUM") RPAR = self._scan("RPAR") return FunctionLiteral(Literal(String.unquoted("opacity=" + NUM)), "alpha") else: # in self.atom_chks argspec = self.argspec() RPAR = self._scan("RPAR") return CallOp("alpha", argspec) elif _token_ == "LITERAL_FUNCTION": LITERAL_FUNCTION = self._scan("LITERAL_FUNCTION") LPAR = self._scan("LPAR") interpolated_function = self.interpolated_function() RPAR = self._scan("RPAR") return Interpolation.maybe(interpolated_function, type=Function, function_name=LITERAL_FUNCTION) elif _token_ == "FNCT": FNCT = self._scan("FNCT") LPAR = self._scan("LPAR") argspec = self.argspec() RPAR = self._scan("RPAR") return CallOp(FNCT, argspec) elif _token_ == "BANG_IMPORTANT": BANG_IMPORTANT = self._scan("BANG_IMPORTANT") return Literal(String.unquoted("!important", literal=True)) elif _token_ in self.atom_chks_: interpolated_bareword = self.interpolated_bareword() return Interpolation.maybe(interpolated_bareword) elif _token_ == "NUM": NUM = self._scan("NUM") UNITS = None if self._peek(self.atom_rsts__) == "UNITS": UNITS = self._scan("UNITS") return Literal(Number(float(NUM), unit=UNITS)) elif _token_ not in self.atom_chks__: interpolated_string = self.interpolated_string() return interpolated_string elif _token_ == "COLOR": COLOR = self._scan("COLOR") return Literal(Color.from_hex(COLOR, literal=True)) else: # == 'VAR' VAR = self._scan("VAR") return Variable(VAR)
def test_subtraction(): assert Number(123) - Number(456) == Number(-333) assert Number(456) - Number(123) == Number(333) # TODO test that subtracting e.g. strings doesn't work assert Color.from_hex('#0f0f0f') - Color.from_hex('#050505') == Color.from_hex('#0a0a0a')
def test_subtraction(calc): assert calc('123 - 456') == Number(-333) assert calc('456 - 123') == Number(333) # TODO test that subtracting e.g. strings doesn't work assert calc('#0f0f0f - #050505') == Color.from_hex('#0a0a0a')