def __init__( self, scss_vars=None, scss_opts=None, scss_files=None, super_selector='', live_errors=False, library=ALL_BUILTINS_LIBRARY, func_registry=None, search_paths=None): self.super_selector = super_selector self._scss_vars = {} if scss_vars: calculator = Calculator() for var_name, value in scss_vars.items(): if isinstance(value, six.string_types): scss_value = calculator.evaluate_expression(value) if scss_value is None: # TODO warning? scss_value = String.unquoted(value) else: scss_value = value self._scss_vars[var_name] = scss_value self._scss_opts = scss_opts or {} self._scss_files = scss_files # NOTE: func_registry is backwards-compatibility for only one user and # has never existed in a real release self._library = func_registry or library self._search_paths = search_paths # If true, swallow compile errors and embed them in the output instead self.live_errors = live_errors
def __init__( self, scss_vars=None, scss_opts=None, scss_files=None, super_selector='', live_errors=False, library=None, func_registry=None, search_paths=None): self.super_selector = super_selector self._scss_vars = {} if scss_vars: calculator = Calculator() for var_name, value in scss_vars.items(): if isinstance(value, six.string_types): scss_value = calculator.evaluate_expression(value) if scss_value is None: # TODO warning? scss_value = String.unquoted(value) else: scss_value = value self._scss_vars[var_name] = scss_value self._scss_opts = scss_opts or {} self._scss_files = scss_files self._library = func_registry or library self._search_paths = search_paths # If true, swallow compile errors and embed them in the output instead self.live_errors = live_errors
def __init__(self, is_sass=False): self.css = Scss() self.namespace = self.css.root_namespace self.options = self.css.scss_opts self.source_file = SourceFile.from_string('', '<shell>', line_numbers=False, is_sass=is_sass) self.calculator = Calculator(self.namespace)
def __init__(self, is_sass=False): # TODO it would be lovely to get these out of here, somehow self.namespace = Namespace(variables=_default_scss_vars) self.compiler = Compiler(namespace=self.namespace) self.compilation = self.compiler.make_compilation() self.legacy_compiler_options = {} self.source_file = SourceFile.from_string('', '<shell>', is_sass=is_sass) self.calculator = Calculator(self.namespace)
def test_functions(calc): ns = Namespace(functions=CORE_LIBRARY) calc = Calculator(ns).calculate assert calc('grayscale(red)') == Color.from_rgb(0.5, 0.5, 0.5) assert calc('grayscale(1)') == String( 'grayscale(1)', quotes=None) # Misusing css built-in functions (with scss counterpart) assert calc('skew(1)') == String( 'skew(1)', quotes=None) # Missing css-only built-in functions with pytest.raises(SassEvaluationError): calc('unitless("X")') # Misusing non-css built-in scss funtions
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!"'
class SassRepl(object): def __init__(self, is_sass=False): self.css = Scss() self.namespace = self.css.root_namespace self.options = self.css.scss_opts self.source_file = SourceFile.from_string('', '<shell>', line_numbers=False, is_sass=is_sass) self.calculator = Calculator(self.namespace) def __call__(self, s): from pprint import pformat if s in ('exit', 'quit'): raise KeyboardInterrupt for s in s.split(';'): s = self.source_file.prepare_source(s.strip()) if not s: continue elif s.startswith('@'): scope = None properties = [] children = deque() rule = SassRule(self.source_file, namespace=self.namespace, options=self.options, properties=properties) block = UnparsedBlock(rule, 1, s, None) code, name = (s.split(None, 1) + [''])[:2] if code == '@option': self.css._settle_options(rule, children, scope, block) continue elif code == '@import': self.css._do_import(rule, children, scope, block) continue elif code == '@include': final_cont = '' self.css._do_include(rule, children, scope, block) code = self.css._print_properties(properties).rstrip('\n') if code: final_cont += code if children: self.css.children.extendleft(children) self.css.parse_children() code = self.css._create_css(self.css.rules).rstrip('\n') if code: final_cont += code yield final_cont continue elif s == 'ls' or s.startswith('show(') or s.startswith('show ') or s.startswith('ls(') or s.startswith('ls '): m = re.match(r'(?:show|ls)(\()?\s*([^,/\\) ]*)(?:[,/\\ ]([^,/\\ )]+))*(?(1)\))', s, re.IGNORECASE) if m: name = m.group(2) code = m.group(3) name = name and name.strip().rstrip('s') # remove last 's' as in functions code = code and code.strip() ns = self.namespace if not name: yield pformat(sorted(['vars', 'options', 'mixins', 'functions'])) elif name in ('v', 'var', 'variable'): variables = dict(ns._variables) if code == '*': pass elif code: variables = dict((k, v) for k, v in variables.items() if code in k) else: variables = dict((k, v) for k, v in variables.items() if not k.startswith('$--')) yield pformat(variables) elif name in ('o', 'opt', 'option'): opts = self.options if code == '*': pass elif code: opts = dict((k, v) for k, v in opts.items() if code in k) else: opts = dict((k, v) for k, v in opts.items() if not k.startswith('@')) yield pformat(opts) elif name in ('m', 'mix', 'mixin', 'f', 'func', 'funct', 'function'): if name.startswith('m'): funcs = dict(ns._mixins) elif name.startswith('f'): funcs = dict(ns._functions) if code == '*': pass elif code: funcs = dict((k, v) for k, v in funcs.items() if code in k[0]) else: pass # TODO print source when possible yield pformat(funcs) continue elif s.startswith('$') and (':' in s or '=' in s): prop, value = [a.strip() for a in _prop_split_re.split(s, 1)] prop = self.calculator.do_glob_math(prop) value = self.calculator.calculate(value) self.namespace.set_variable(prop, value) continue # TODO respect compress? try: yield(self.calculator.calculate(s).render()) except (SyntaxError, SassEvaluationError) as e: print("%s" % e, file=sys.stderr)
class SassRepl(object): def __init__(self, is_sass=False): self.css = Scss() self.namespace = self.css.root_namespace self.options = self.css.scss_opts self.source_file = SourceFile.from_string('', '<shell>', line_numbers=False, is_sass=is_sass) self.calculator = Calculator(self.namespace) def __call__(self, s): from pprint import pformat if s in ('exit', 'quit'): raise KeyboardInterrupt for s in s.split(';'): s = self.source_file.prepare_source(s.strip()) if not s: continue elif s.startswith('@'): scope = None properties = [] children = deque() rule = SassRule(self.source_file, namespace=self.namespace, options=self.options, properties=properties) block = UnparsedBlock(rule, 1, s, None) code, name = (s.split(None, 1) + [''])[:2] if code == '@option': self.css._at_options(self.calculator, rule, scope, block) continue elif code == '@import': self.css._at_import(self.calculator, rule, scope, block) continue elif code == '@include': final_cont = '' self.css._at_include(self.calculator, rule, scope, block) code = self.css._print_properties(properties).rstrip('\n') if code: final_cont += code if children: self.css.children.extendleft(children) self.css.parse_children() code = self.css._create_css( self.css.rules).rstrip('\n') if code: final_cont += code yield final_cont continue elif s == 'ls' or s.startswith('show(') or s.startswith( 'show ') or s.startswith('ls(') or s.startswith('ls '): m = re.match( r'(?:show|ls)(\()?\s*([^,/\\) ]*)(?:[,/\\ ]([^,/\\ )]+))*(?(1)\))', s, re.IGNORECASE) if m: name = m.group(2) code = m.group(3) name = name and name.strip().rstrip( 's') # remove last 's' as in functions code = code and code.strip() ns = self.namespace if not name: yield pformat( sorted(['vars', 'options', 'mixins', 'functions'])) elif name in ('v', 'var', 'variable'): variables = dict(ns._variables) if code == '*': pass elif code: variables = dict((k, v) for k, v in variables.items() if code in k) else: variables = dict((k, v) for k, v in variables.items() if not k.startswith('$--')) yield pformat(variables) elif name in ('o', 'opt', 'option'): opts = self.options if code == '*': pass elif code: opts = dict( (k, v) for k, v in opts.items() if code in k) else: opts = dict((k, v) for k, v in opts.items() if not k.startswith('@')) yield pformat(opts) elif name in ('m', 'mix', 'mixin', 'f', 'func', 'funct', 'function'): if name.startswith('m'): funcs = dict(ns._mixins) elif name.startswith('f'): funcs = dict(ns._functions) if code == '*': pass elif code: funcs = dict((k, v) for k, v in funcs.items() if code in k[0]) else: pass # TODO print source when possible yield pformat(funcs) continue elif s.startswith('$') and (':' in s or '=' in s): prop, value = [a.strip() for a in _prop_split_re.split(s, 1)] prop = self.calculator.do_glob_math(prop) value = self.calculator.calculate(value) self.namespace.set_variable(prop, value) continue # TODO respect compress? try: yield (self.calculator.calculate(s).render()) except (SyntaxError, SassEvaluationError) as e: print("%s" % e, file=sys.stderr)
def calc(): ns = Namespace(functions=COMPASS_HELPERS_LIBRARY) return Calculator(ns).evaluate_expression
def calc(): return Calculator().evaluate_expression
class SassRepl(object): def __init__(self, is_sass=False): self.css = Scss() self.namespace = self.css.root_namespace self.options = self.css.scss_opts self.source_file = SourceFile.from_string("", "<shell>", line_numbers=False, is_sass=is_sass) self.calculator = Calculator(self.namespace) def __call__(self, s): from pprint import pformat if s in ("exit", "quit"): raise KeyboardInterrupt for s in s.split(";"): s = self.source_file.prepare_source(s.strip()) if not s: continue elif s.startswith("@"): scope = None properties = [] children = deque() rule = SassRule(self.source_file, namespace=self.namespace, options=self.options, properties=properties) block = UnparsedBlock(rule, 1, s, None) code, name = (s.split(None, 1) + [""])[:2] if code == "@option": self.css._settle_options(rule, children, scope, block) continue elif code == "@import": self.css._do_import(rule, children, scope, block) continue elif code == "@include": final_cont = "" self.css._do_include(rule, children, scope, block) code = self.css._print_properties(properties).rstrip("\n") if code: final_cont += code if children: self.css.children.extendleft(children) self.css.parse_children() code = self.css._create_css(self.css.rules).rstrip("\n") if code: final_cont += code yield final_cont continue elif ( s == "ls" or s.startswith("show(") or s.startswith("show ") or s.startswith("ls(") or s.startswith("ls ") ): m = re.match(r"(?:show|ls)(\()?\s*([^,/\\) ]*)(?:[,/\\ ]([^,/\\ )]+))*(?(1)\))", s, re.IGNORECASE) if m: name = m.group(2) code = m.group(3) name = name and name.strip().rstrip("s") # remove last 's' as in functions code = code and code.strip() ns = self.namespace if not name: yield pformat(sorted(["vars", "options", "mixins", "functions"])) elif name in ("v", "var", "variable"): variables = dict(ns._variables) if code == "*": pass elif code: variables = dict((k, v) for k, v in variables.items() if code in k) else: variables = dict((k, v) for k, v in variables.items() if not k.startswith("$--")) yield pformat(variables) elif name in ("o", "opt", "option"): opts = self.options if code == "*": pass elif code: opts = dict((k, v) for k, v in opts.items() if code in k) else: opts = dict((k, v) for k, v in opts.items() if not k.startswith("@")) yield pformat(opts) elif name in ("m", "mix", "mixin", "f", "func", "funct", "function"): if name.startswith("m"): funcs = dict(ns._mixins) elif name.startswith("f"): funcs = dict(ns._functions) if code == "*": pass elif code: funcs = dict((k, v) for k, v in funcs.items() if code in k[0]) else: pass # TODO print source when possible yield pformat(funcs) continue elif s.startswith("$") and (":" in s or "=" in s): prop, value = [a.strip() for a in _prop_split_re.split(s, 1)] prop = self.calculator.do_glob_math(prop) value = self.calculator.calculate(value) self.namespace.set_variable(prop, value) continue # TODO respect compress? yield (self.calculator.calculate(s).render())