Example #1
0
    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
Example #2
0
    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
Example #3
0
 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)
Example #4
0
    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)
Example #5
0
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
Example #6
0
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!"'
Example #7
0
 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)
Example #8
0
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)
Example #9
0
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)
Example #10
0
def calc():
    ns = Namespace(functions=COMPASS_HELPERS_LIBRARY)
    return Calculator(ns).evaluate_expression
Example #11
0
def calc():
    return Calculator().evaluate_expression
Example #12
0
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())