Ejemplo n.º 1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable('$static_url', String(settings.STATIC_URL))
Ejemplo n.º 2
0
    def __init__(self, *args, **kwargs):
        super(HorizonScssFilter, self).__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable('$static_url', String(six.text_type(getattr(settings, 'STATIC_URL', '/static/'))))
Ejemplo n.º 3
0
    def style_scss(self, *path):
        css_namespace = Namespace()
        for key, value in self.settings['keys'].items():
            if isinstance(value, LCText):
                css_value = String(value)
            elif isinstance(value, LCColour):
                css_value = Color.from_hex(value)
            elif isinstance(value, LCBool):
                css_value = Boolean(value.simple())
            elif isinstance(value, LCSpin):
                css_value = Number(value.simple())
            else:
                raise ValueError("Unable to find comparable values")
            css_namespace.set_variable('${}'.format(key), css_value)

        cherrypy.response.headers['Content-Type'] = 'text/css'
        with open(os.path.join(self.settings['location'], *path), 'r') as css:
            css_content = css.read()
            compiler = Compiler(namespace=css_namespace, output_style='nested')
            # Something wrong with PyScss,
            #  Syntax error: Found u'100%' but expected one of ADD.
            # Doesn't happen on next attempt, so we are doing bad thing
            attempts = 0
            while attempts < 100:
                try:
                    attempts += 1
                    ret_string = compiler.compile_string(css_content)
                    return ret_string
                except Exception as exc:
                    if attempts == 100:
                        log.debug(exc)
Ejemplo n.º 4
0
def styles(request, name):
    src = ''
    namespace = Namespace()
    for tv in ThemeValue.objects.all():
        namespace.set_variable('${}-{}'.format(tv.group, tv.name), String(tv.value))
    compiler = Compiler(namespace=namespace)
    return HttpResponse(compiler.compile_string(src), content_type='text/css')
Ejemplo n.º 5
0
def styles(request, name):
    src = ''
    namespace = Namespace()
    for tv in ThemeValue.objects.all():
        namespace.set_variable('${}-{}'.format(tv.group, tv.name),
                               String(tv.value))
    compiler = Compiler(namespace=namespace)
    return HttpResponse(compiler.compile_string(src), content_type='text/css')
    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)
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
class HorizonScssFilter(DjangoScssFilter):
    def __init__(self, *args, **kwargs):
        super(HorizonScssFilter, self).__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable('$static_url', String(six.text_type(getattr(settings, 'STATIC_URL', '/static/'))))

    # Create a compiler with the right namespace
    @property
    def compiler(self):
        return DjangoScssCompiler(namespace=self.namespace)
Ejemplo n.º 9
0
    def __init__(self,
                 source_file,
                 import_key=None,
                 unparsed_contents=None,
                 num_header_lines=0,
                 options=None,
                 legacy_compiler_options=None,
                 properties=None,
                 namespace=None,
                 lineno=0,
                 extends_selectors=frozenset(),
                 ancestry=None,
                 nested=0,
                 from_source_file=None,
                 from_lineno=0):

        self.from_source_file = from_source_file
        self.from_lineno = from_lineno

        self.source_file = source_file
        self.import_key = import_key
        self.lineno = lineno

        self.num_header_lines = num_header_lines
        self.unparsed_contents = unparsed_contents
        self.legacy_compiler_options = legacy_compiler_options or {}
        self.options = options or {}
        self.extends_selectors = extends_selectors

        if namespace is None:
            assert False
            self.namespace = Namespace()
        else:
            self.namespace = namespace

        if properties is None:
            self.properties = []
        else:
            self.properties = properties

        if ancestry is None:
            self.ancestry = RuleAncestry()
        else:
            self.ancestry = ancestry

        self.nested = nested

        self.descendants = 0
Ejemplo n.º 10
0
class CoreExtension(Extension):
    name = 'core'
    namespace = Namespace()

    def handle_import(self, name, compilation, rule):
        """Implementation of the core Sass import mechanism, which just looks
        for files on disk.
        """
        # TODO this is all not terribly well-specified by Sass.  at worst,
        # it's unclear how far "upwards" we should be allowed to go.  but i'm
        # also a little fuzzy on e.g. how relative imports work from within a
        # file that's not actually in the search path.
        # TODO i think with the new origin semantics, i've made it possible to
        # import relative to the current file even if the current file isn't
        # anywhere in the search path.  is that right?
        path = PurePosixPath(name)
        if path.suffix:
            search_exts = [path.suffix]
        else:
            search_exts = compilation.compiler.dynamic_extensions

        basename = path.stem
        relative_to = path.parent
        search_path = []  # tuple of (origin, start_from)
        if relative_to.is_absolute():
            relative_to = PurePosixPath(*relative_to.parts[1:])
        elif rule.source_file.origin:
            # Search relative to the current file first, only if not doing an
            # absolute import
            search_path.append((
                rule.source_file.origin,
                rule.source_file.relpath.parent / relative_to,
            ))
        search_path.extend(
            (origin, relative_to)
            for origin in compilation.compiler.search_path
        )

        for prefix, suffix in product(('_', ''), search_exts):
            filename = prefix + basename + suffix
            for origin, relative_to in search_path:
                relpath = relative_to / filename
                # Lexically (ignoring symlinks!) eliminate .. from the part
                # of the path that exists within Sass-space.  pathlib
                # deliberately doesn't do this, but os.path does.
                relpath = PurePosixPath(os.path.normpath(str(relpath)))

                if rule.source_file.key == (origin, relpath):
                    # Avoid self-import
                    # TODO is this what ruby does?
                    continue

                path = origin / relpath
                if not path.exists():
                    continue

                # All good!
                # TODO if this file has already been imported, we'll do the
                # source preparation twice.  make it lazy.
                return SourceFile.read(origin, relpath)
Ejemplo n.º 11
0
class HorizonScssFilter(DjangoScssFilter):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable('$static_url', String(settings.STATIC_URL))

    # Create a compiler with the right namespace
    @property
    def compiler(self):
        return DjangoScssCompiler(
            # output_style is 'nested' by default, which is crazy. See:
            # https://github.com/Kronuz/pyScss/issues/243
            output_style='compact',  # or 'compressed'
            namespace=self.namespace)
Ejemplo n.º 12
0
class HorizonScssFilter(DjangoScssFilter):
    def __init__(self, *args, **kwargs):
        super(HorizonScssFilter, self).__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable(
            '$static_url',
            String(six.text_type(getattr(settings, 'STATIC_URL', '/static/')))
        )

    # Create a compiler with the right namespace
    @property
    def compiler(self):
        return DjangoScssCompiler(
            namespace=self.namespace
        )
Ejemplo n.º 13
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)
Ejemplo n.º 14
0
    def __init__(self, *args, **kwargs):
        super(HorizonScssFilter, self).__init__(*args, **kwargs)

        self.namespace = Namespace()

        # Add variables to the SCSS Global Namespace Here
        self.namespace.set_variable(
            '$static_url',
            String(six.text_type(getattr(settings, 'STATIC_URL', '/static/')))
        )
Ejemplo n.º 15
0
class CoreExtension(Extension):
    name = 'core'
    namespace = Namespace()

    def handle_import(self, name, compilation, rule):
        """Implementation of the core Sass import mechanism, which just looks
        for files on disk.
        """
        name, ext = os.path.splitext(name)
        if ext:
            search_exts = [ext]
        else:
            search_exts = ['.scss', '.sass']

        dirname, basename = os.path.split(name)

        # Search relative to the importing file first
        search_path = [
            os.path.normpath(os.path.abspath(
                os.path.dirname(rule.source_file.path)))]
        search_path.extend(compilation.compiler.search_path)

        for prefix, suffix in product(('_', ''), search_exts):
            filename = prefix + basename + suffix
            for directory in search_path:
                path = os.path.normpath(
                    os.path.join(directory, dirname, filename))

                if path == rule.source_file.path:
                    # Avoid self-import
                    # TODO is this what ruby does?
                    continue

                if not os.path.exists(path):
                    continue

                # Ensure that no one used .. to escape the search path
                for valid_path in compilation.compiler.search_path:
                    rel = os.path.relpath(path, start=valid_path)
                    if not rel.startswith('../'):
                        break
                else:
                    continue

                # All good!
                return SourceFile.from_filename(path)
Ejemplo n.º 16
0
    def __init__(
            self, source_file, import_key=None, unparsed_contents=None,
            num_header_lines=0,
            options=None, legacy_compiler_options=None, properties=None,
            namespace=None,
            lineno=0, extends_selectors=frozenset(),
            ancestry=None,
            nested=0,
            from_source_file=None, from_lineno=0):

        self.from_source_file = from_source_file
        self.from_lineno = from_lineno

        self.source_file = source_file
        self.import_key = import_key
        self.lineno = lineno

        self.num_header_lines = num_header_lines
        self.unparsed_contents = unparsed_contents
        self.legacy_compiler_options = legacy_compiler_options or {}
        self.options = options or {}
        self.extends_selectors = extends_selectors

        if namespace is None:
            assert False
            self.namespace = Namespace()
        else:
            self.namespace = namespace

        if properties is None:
            self.properties = []
        else:
            self.properties = properties

        if ancestry is None:
            self.ancestry = RuleAncestry()
        else:
            self.ancestry = ancestry

        self.nested = nested

        self.descendants = 0
Ejemplo n.º 17
0
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division

import os.path

from scss.namespace import Namespace
from scss.types import Boolean
from scss.types import List
from scss.types import Null
from scss.types import String
from scss.types import expect_type

# TODO would like to just add a new /child/ to an existing namespace, i think,
# so i don't have to do an import dance?  maybe?
ns = Namespace()


@ns.declare
def join_file_segments(*segments):
    """Join path parts into a single path, using the appropriate OS-specific
    delimiter.
    """
    parts = []
    for segment in segments:
        expect_type(segment, String)
        parts.append(segment.value)

    if parts:
        return String(os.path.join(*parts))
    else:
Ejemplo n.º 18
0
class SassRule(object):
    """At its heart, a CSS rule: combination of a selector and zero or more
    properties.  But this is Sass, so it also tracks some Sass-flavored
    metadata, like `@extend` rules and `@media` nesting.
    """
    def __init__(self,
                 source_file,
                 import_key=None,
                 unparsed_contents=None,
                 options=None,
                 legacy_compiler_options=None,
                 properties=None,
                 namespace=None,
                 lineno=0,
                 extends_selectors=frozenset(),
                 ancestry=None,
                 nested=0,
                 from_source_file=None,
                 from_lineno=0):

        self.from_source_file = from_source_file
        self.from_lineno = from_lineno

        self.source_file = source_file
        self.import_key = import_key
        self.lineno = lineno

        self.unparsed_contents = unparsed_contents
        self.legacy_compiler_options = legacy_compiler_options or {}
        self.options = options or {}
        self.extends_selectors = extends_selectors

        if namespace is None:
            assert False
            self.namespace = Namespace()
        else:
            self.namespace = namespace

        if properties is None:
            self.properties = []
        else:
            self.properties = properties

        if ancestry is None:
            self.ancestry = RuleAncestry()
        else:
            self.ancestry = ancestry

        self.nested = nested

        self.descendants = 0

    def __repr__(self):
        return "<SassRule %s, %d props>" % (
            self.ancestry,
            len(self.properties),
        )

    @property
    def selectors(self):
        # TEMPORARY
        if self.ancestry.headers and self.ancestry.headers[-1].is_selector:
            return self.ancestry.headers[-1].selectors
        else:
            return ()

    @property
    def file_and_line(self):
        """Return the filename and line number where this rule originally
        appears, in the form "foo.scss:3".  Used for error messages.
        """
        ret = "%s:%d" % (self.source_file.path, self.lineno)
        if self.from_source_file:
            ret += " (%s:%d)" % (self.from_source_file.path, self.from_lineno)
        return ret

    @property
    def is_empty(self):
        """Return whether this rule is considered "empty" -- i.e., has no
        contents that should end up in the final CSS.
        """
        if self.properties:
            # Rules containing CSS properties are never empty
            return False

        if not self.descendants:
            for header in self.ancestry.headers:
                if header.is_atrule and header.directive != '@media':
                    # At-rules should always be preserved, UNLESS they are @media
                    # blocks, which are known to be noise if they don't have any
                    # contents of their own
                    return False

        return True

    @property
    def is_pure_placeholder(self):
        selectors = self.selectors
        if not selectors:
            return False
        for s in selectors:
            if not s.has_placeholder:
                return False
        return True

    def copy(self):
        return type(self)(
            source_file=self.source_file,
            lineno=self.lineno,
            from_source_file=self.from_source_file,
            from_lineno=self.from_lineno,
            unparsed_contents=self.unparsed_contents,
            legacy_compiler_options=self.legacy_compiler_options,
            options=self.options,
            #properties=list(self.properties),
            properties=self.properties,
            extends_selectors=self.extends_selectors,
            #ancestry=list(self.ancestry),
            ancestry=self.ancestry,
            namespace=self.namespace.derive(),
            nested=self.nested,
        )
Ejemplo n.º 19
0
class BootstrapExtension(Extension):
    name = 'bootstrap'
    namespace = Namespace()
Ejemplo n.º 20
0
class SassRepl(object):
    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 __call__(self, s):
        # TODO this is kind of invasive; surely it's possible to do this
        # without calling only private methods
        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,
                    legacy_compiler_options=self.legacy_compiler_options,
                    properties=properties)
                block = UnparsedBlock(rule, 1, s, None)
                code, name = (s.split(None, 1) + [''])[:2]
                if code == '@option':
                    self.compilation._at_options(self.calculator, rule, scope,
                                                 block)
                    continue
                elif code == '@import':
                    self.compilation._at_import(self.calculator, rule, scope,
                                                block)
                    continue
                elif code == '@include':
                    final_cont = ''
                    self.compilation._at_include(self.calculator, rule, scope,
                                                 block)
                    code = self.compilation._print_properties(
                        properties).rstrip('\n')
                    if code:
                        final_cont += code
                    if children:
                        self.compilation.children.extendleft(children)
                        self.compilation.parse_children()
                        code = self.compilation._create_css(
                            self.compilation.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(
                            list(
                                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.legacy_compiler_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())
                        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)
Ejemplo n.º 21
0
#! /usr/bin/env python3

from scss.namespace import Namespace
from scss.types import String
from scss import compiler
import glob
import os

scss_list = glob.glob("gnome-shell/*.scss")
print("\nSCSS_FILES:", scss_list)
for scss_path in scss_list:
	css_path = scss_path.replace("scss", "css")
	print("\nscss path:", scss_path)
	print("css path:", css_path)
	
	namespace = Namespace()
	namespace.set_variable("$variant", String("light"))
	css_data = compiler.compile_file(scss_path, namespace=namespace)
	with open(css_path, "w") as css_file:
		css_file.write(css_data)

Ejemplo n.º 22
0
"""Compile static assets."""
from flask import current_app as app
from flask_assets import Bundle
from scss import Compiler
from scss.namespace import Namespace

namespace = Namespace()
compiler = Compiler(namespace=namespace)

# Namespace variables


def pyscss_filter(_in, out, **kw):
    """
    Custom pyscss filter, I forgot why, but I know its needed
    :param _in: Input stream that reads the scss file
    :param out: Out stream that writes to the css file
    :param kw:
    :return:
    """
    out.write(compiler.compile_string(_in.read()))


def compile_static_assets(assets):
    """
    Create stylesheet bundles.
    :param assets: Flask-Assets Environment
    :type assets: Environment
    """
    assets.auto_build = True
    assets.debug = True
Ejemplo n.º 23
0
def render_scss_file(filename, namespace):
    root_dir = os.path.dirname(filename)
    base_name, base_extension = os.path.splitext(os.path.basename(filename))
    render_filename = os.path.join(root_dir, 'render_{}.css'.format(base_name))
    with open(filename, 'r') as css_file, open(render_filename, 'w') as render_file:
        css_content = css_file.read()
        compiler = Compiler(namespace=namespace, output_style='compressed')
        render_file.write(compiler.compile_string(css_content))

for folder in os.listdir('http'):
    # Load keys
    cur_path = os.path.join('http', folder)
    with open(os.path.join(cur_path, 'settings.json'), 'r') as json_settings:
        setting_keys = json.load(json_settings)

    css_namespace = Namespace()
    for key, value in setting_keys.items():
        for base_class, scss_class in SCSS_MAP.items():
            if isinstance(value, base_class):
                css_namespace.set_variable('${}'.format(key), scss_class(value))
                break

    for item in os.listdir(os.path.join(cur_path, 'css')):
        if item.endswith('.css'):
            pass
        elif item.endswith('.scss'):
            render_scss_file(os.path.join(cur_path, 'css', item), css_namespace)
        else:
            pass
Ejemplo n.º 24
0
    def compile(self,
                scss_string=None,
                scss_file=None,
                source_files=None,
                super_selector=None,
                filename=None,
                is_sass=None,
                line_numbers=True,
                import_static_css=False):
        """Compile Sass to CSS.  Returns a single CSS string.

        This method is DEPRECATED; see :mod:`scss.compiler` instead.
        """
        # Derive our root namespace
        self.scss_vars = _default_scss_vars.copy()
        if self._scss_vars is not None:
            self.scss_vars.update(self._scss_vars)

        root_namespace = Namespace(
            variables=self.scss_vars,
            functions=self._library,
        )

        # Figure out search paths.  Fall back from provided explicitly to
        # defined globally to just searching the current directory
        search_paths = ['.']
        if self._search_paths is not None:
            assert not isinstance(self._search_paths, six.string_types), \
                "`search_paths` should be an iterable, not a string"
            search_paths.extend(self._search_paths)
        else:
            if config.LOAD_PATHS:
                if isinstance(config.LOAD_PATHS, six.string_types):
                    # Back-compat: allow comma-delimited
                    search_paths.extend(config.LOAD_PATHS.split(','))
                else:
                    search_paths.extend(config.LOAD_PATHS)

            search_paths.extend(self._scss_opts.get('load_paths', []))

        # Normalize a few old styles of options
        output_style = self._scss_opts.get('style', config.STYLE)
        if output_style is True:
            output_style = 'compressed'
        elif output_style is False:
            output_style = 'legacy'

        fixed_search_path = []
        for path in search_paths:
            if isinstance(path, six.string_types):
                fixed_search_path.append(Path(path))
            else:
                fixed_search_path.append(path)

        # Build the compiler
        compiler = Compiler(
            namespace=root_namespace,
            extensions=[
                CoreExtension,
                ExtraExtension,
                FontsExtension,
                CompassExtension,
                BootstrapExtension,
            ],
            search_path=fixed_search_path,
            import_static_css=import_static_css,
            live_errors=self.live_errors,
            generate_source_map=self._scss_opts.get('debug_info', False),
            output_style=output_style,
            warn_unused_imports=self._scss_opts.get('warn_unused', False),
            ignore_parse_errors=config.DEBUG,
            loops_have_own_scopes=config.CONTROL_SCOPING,
            undefined_variables_fatal=config.FATAL_UNDEFINED,
            super_selector=super_selector or self.super_selector,
        )
        # Gonna add the source files manually
        compilation = compiler.make_compilation()

        # Inject the files we know about
        # TODO how does this work with the expectation of absoluteness
        if source_files is not None:
            for source in source_files:
                compilation.add_source(source)
        elif scss_string is not None:
            source = SourceFile.from_string(
                scss_string,
                relpath=filename,
                is_sass=is_sass,
            )
            compilation.add_source(source)
        elif scss_file is not None:
            # This is now the only way to allow forcibly overriding the
            # filename a source "thinks" it is
            with open(scss_file, 'rb') as f:
                source = SourceFile.from_file(
                    f,
                    relpath=filename or scss_file,
                    is_sass=is_sass,
                )
            compilation.add_source(source)

        # Plus the ones from the constructor
        if self._scss_files:
            for name, contents in list(self._scss_files.items()):
                source = SourceFile.from_string(contents, relpath=name)
                compilation.add_source(source)

        return compiler.call_and_catch_errors(compilation.run)
Ejemplo n.º 25
0
class SassRule(object):
    """At its heart, a CSS rule: combination of a selector and zero or more
    properties.  But this is Sass, so it also tracks some Sass-flavored
    metadata, like `@extend` rules and `@media` nesting.
    """

    def __init__(
            self, source_file, import_key=None, unparsed_contents=None,
            num_header_lines=0,
            options=None, legacy_compiler_options=None, properties=None,
            namespace=None,
            lineno=0, extends_selectors=frozenset(),
            ancestry=None,
            nested=0,
            from_source_file=None, from_lineno=0):

        self.from_source_file = from_source_file
        self.from_lineno = from_lineno

        self.source_file = source_file
        self.import_key = import_key
        self.lineno = lineno

        self.num_header_lines = num_header_lines
        self.unparsed_contents = unparsed_contents
        self.legacy_compiler_options = legacy_compiler_options or {}
        self.options = options or {}
        self.extends_selectors = extends_selectors

        if namespace is None:
            assert False
            self.namespace = Namespace()
        else:
            self.namespace = namespace

        if properties is None:
            self.properties = []
        else:
            self.properties = properties

        if ancestry is None:
            self.ancestry = RuleAncestry()
        else:
            self.ancestry = ancestry

        self.nested = nested

        self.descendants = 0

    def __repr__(self):
        # TODO probably want to encode this with string_escape on python 2, and
        # similar elsewhere, especially since this file has unicode_literals
        return "<SassRule %s, %d props>" % (
            self.ancestry,
            len(self.properties),
        )

    @property
    def selectors(self):
        # TEMPORARY
        if self.ancestry.headers and self.ancestry.headers[-1].is_selector:
            return self.ancestry.headers[-1].selectors
        else:
            return ()

    @property
    def file_and_line(self):
        """Return the filename and line number where this rule originally
        appears, in the form "foo.scss:3".  Used for error messages.
        """
        ret = "%s:%d" % (self.source_file.path, self.lineno)
        if self.from_source_file:
            ret += " (%s:%d)" % (self.from_source_file.path, self.from_lineno)
        return ret

    @property
    def is_empty(self):
        """Return whether this rule is considered "empty" -- i.e., has no
        contents that should end up in the final CSS.
        """
        if self.properties:
            # Rules containing CSS properties are never empty
            return False

        if not self.descendants:
            for header in self.ancestry.headers:
                if header.is_atrule and header.directive != '@media':
                    # At-rules should always be preserved, UNLESS they are @media
                    # blocks, which are known to be noise if they don't have any
                    # contents of their own
                    return False

        return True

    @property
    def is_pure_placeholder(self):
        selectors = self.selectors
        if not selectors:
            return False
        for s in selectors:
            if not s.has_placeholder:
                return False
        return True


    def copy(self):
        return type(self)(
            source_file=self.source_file,
            lineno=self.lineno,

            from_source_file=self.from_source_file,
            from_lineno=self.from_lineno,

            unparsed_contents=self.unparsed_contents,

            legacy_compiler_options=self.legacy_compiler_options,
            options=self.options,
            #properties=list(self.properties),
            properties=self.properties,
            extends_selectors=self.extends_selectors,
            #ancestry=list(self.ancestry),
            ancestry=self.ancestry,

            namespace=self.namespace.derive(),
            nested=self.nested,
        )
Ejemplo n.º 26
0
class SassRepl(object):
    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 __call__(self, s):
        # TODO this is kind of invasive; surely it's possible to do this
        # without calling only private methods
        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, legacy_compiler_options=self.legacy_compiler_options, properties=properties)
                block = UnparsedBlock(rule, 1, s, None)
                code, name = (s.split(None, 1) + [''])[:2]
                if code == '@option':
                    self.compilation._at_options(self.calculator, rule, scope, block)
                    continue
                elif code == '@import':
                    self.compilation._at_import(self.calculator, rule, scope, block)
                    continue
                elif code == '@include':
                    final_cont = ''
                    self.compilation._at_include(self.calculator, rule, scope, block)
                    code = self.compilation._print_properties(properties).rstrip('\n')
                    if code:
                        final_cont += code
                    if children:
                        self.compilation.children.extendleft(children)
                        self.compilation.parse_children()
                        code = self.compilation._create_css(self.compilation.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(list(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.legacy_compiler_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())
                        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)
Ejemplo n.º 27
0
class CompassExtension(Extension):
    name = 'compass'
    namespace = Namespace()

    def handle_import(self, name, compilation, rule):
        """Implementation of Compass's "magic" imports, which generate
        spritesheets on the fly, given either a wildcard or the name of a
        directory.
        """
        from .sprites import sprite_map

        # TODO check that the found file is actually under the root
        if callable(config.STATIC_ROOT):
            files = sorted(config.STATIC_ROOT(name))
        else:
            glob_path = os.path.join(config.STATIC_ROOT, name)
            files = glob.glob(glob_path)
            files = sorted(
                (fn[len(config.STATIC_ROOT):], None) for fn in files)

        if not files:
            return

        # Build magic context
        calculator = compilation._make_calculator(rule.namespace)
        map_name = os.path.normpath(os.path.dirname(name)).replace(
            '\\', '_').replace('/', '_')
        kwargs = {}

        # TODO this is all kinds of busted.  rule.context hasn't existed for
        # ages.
        def setdefault(var, val):
            _var = '$' + map_name + '-' + var
            if _var in rule.context:
                kwargs[var] = calculator.interpolate(rule.context[_var], rule,
                                                     self._library)
            else:
                rule.context[_var] = val
                kwargs[var] = calculator.interpolate(val, rule, self._library)
            return rule.context[_var]

        setdefault('sprite-base-class',
                   String('.' + map_name + '-sprite', quotes=None))
        setdefault('sprite-dimensions', Boolean(False))
        position = setdefault('position', Number(0, '%'))
        spacing = setdefault('spacing', Number(0))
        repeat = setdefault('repeat', String('no-repeat', quotes=None))
        names = tuple(
            os.path.splitext(os.path.basename(file))[0]
            for file, storage in files)
        for n in names:
            setdefault(n + '-position', position)
            setdefault(n + '-spacing', spacing)
            setdefault(n + '-repeat', repeat)
        rule.context['$' + map_name + '-' + 'sprites'] = sprite_map(
            name, **kwargs)
        generated_code = '''
            @import "compass/utilities/sprites/base";

            // All sprites should extend this class
            // The %(map_name)s-sprite mixin will do so for you.
            #{$%(map_name)s-sprite-base-class} {
                background: $%(map_name)s-sprites;
            }

            // Use this to set the dimensions of an element
            // based on the size of the original image.
            @mixin %(map_name)s-sprite-dimensions($name) {
                @include sprite-dimensions($%(map_name)s-sprites, $name);
            }

            // Move the background position to display the sprite.
            @mixin %(map_name)s-sprite-position($name, $offset-x: 0, $offset-y: 0) {
                @include sprite-position($%(map_name)s-sprites, $name, $offset-x, $offset-y);
            }

            // Extends the sprite base class and set the background position for the desired sprite.
            // It will also apply the image dimensions if $dimensions is true.
            @mixin %(map_name)s-sprite($name, $dimensions: $%(map_name)s-sprite-dimensions, $offset-x: 0, $offset-y: 0) {
                @extend #{$%(map_name)s-sprite-base-class};
                @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y);
            }

            @mixin %(map_name)s-sprites($sprite-names, $dimensions: $%(map_name)s-sprite-dimensions) {
                @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class, $dimensions);
            }

            // Generates a class for each sprited image.
            @mixin all-%(map_name)s-sprites($dimensions: $%(map_name)s-sprite-dimensions) {
                @include %(map_name)s-sprites(%(sprites)s, $dimensions);
            }
        ''' % {
            'map_name': map_name,
            'sprites': ' '.join(names)
        }

        return SourceFile.from_string(generated_code)
Ejemplo n.º 28
0
class FontsExtension(Extension):
    """Functions for creating and manipulating fonts."""
    name = 'fonts'
    namespace = Namespace()
Ejemplo n.º 29
0
class ExtraExtension(Extension):
    """Extra functions unique to the pyScss library."""
    name = 'extra'
    namespace = Namespace()