Ejemplo n.º 1
0
    def __init__(self, app, static_dir=None, asset_dir=None, load_paths=None):
        '''

        See :ref:`scss_discovery_rules`
        and :ref:`static_discovery_rules`
        for more information about the impact of ``static_dir`` and
        ``asset_dir`` parameters.

        Parameters here has preedence over Parameters found in the application
        config.

        :param app: Your Flask Application
        :param static_dir: The path to the ``static`` directory of your
                           application (optional)
        :param asset_dir: The path to the ``assets`` directory where Flask-Scss
                          will search ``.scss`` files (optional)
        :param load_paths: A list of folders to add to pyScss load_paths
                           (for ex., the path to a library like Compass)
        '''
        if not load_paths:
            load_paths = []

        self.app = app
        self.asset_dir = self.set_asset_dir(asset_dir)
        self.static_dir = self.set_static_dir(static_dir)
        self.assets = {}
        self.partials = {}

        load_path_list = ([self.asset_dir] if self.asset_dir else []) \
                       + (load_paths or app.config.get('SCSS_LOAD_PATHS', []))

        # pyScss.log = app.logger
        self.compiler = Compiler(search_path=load_path_list)
        if self.app.testing or self.app.debug:
            self.set_hooks()
Ejemplo n.º 2
0
 def clean(self):
     compiler = Compiler(search_path=getattr(settings, 'SASS_SEARCH_PATHS', []),
                         output_style='compressed' if self.minify else 'nested')
     try:
         self.output = compiler.compile_string(self.source)
     except (ValueError, SassSyntaxError) as e:
         import traceback ; traceback.print_exc()
         raise ValidationError({'source': str(e)})
Ejemplo n.º 3
0
 def _compile(self, files, debug=False):
     compiler = Compiler(
         search_path=self._get_search_path_for_files(files),
         output_style=debug and 'expanded' or 'compressed',
         generate_source_map=True,
         namespace=self.namespace)
     compiler.extensions.append(ThemingExtensions())
     css = compiler.compile_sources(*files)
     return css.encode('utf-8')
    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.º 5
0
 def clean(self):
     compiler = Compiler(
         search_path=getattr(settings, 'SASS_SEARCH_PATHS', []),
         output_style='compressed' if self.minify else 'nested')
     try:
         self.output = compiler.compile_string(self.source)
     except (ValueError, SassSyntaxError) as e:
         import traceback
         traceback.print_exc()
         raise ValidationError({'source': str(e)})
Ejemplo n.º 6
0
    def setup(self):
        try:
            from scss.compiler import Compiler
        except ImportError:
            raise EnvironmentError('The "pyScss" package is not installed.')
        else:

            search_path = [
                path.join(path.abspath(path.dirname(__file__)), 'static', 'scss')
            ]
            self.compiler = Compiler(search_path=search_path)
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
    def compile_file(self, path: str):
        """Compiles SCSS into CSS."""

        # don't compile non-scss files
        if os.path.splitext(path)[1] != ".scss":
            return

        # the name of the compiled file without extensions
        name = Path(path).stem
        output_path = os.path.join(SCSSAutoCompiler.OUTPUT_DIR, name + ".css")

        try:
            # ensure these folders exist
            os.makedirs(os.path.dirname(output_path))
        except FileExistsError:
            pass

        try:
            print(f"compiling SCSS at {path} => {output_path}")
            with open(output_path, "w") as f:
                f.write(Compiler(search_path=("style/", )).compile(path))
            self.octogon.on_compile(name, "scss", output_path)
        except Exception as e:
            print("failed to compile scss: ")
            print(e)
Ejemplo n.º 9
0
    def __init__(self, app, static_dir=None, asset_dir=None, load_paths=None):
        '''

        See :ref:`scss_discovery_rules`
        and :ref:`static_discovery_rules`
        for more information about the impact of ``static_dir`` and
        ``asset_dir`` parameters.

        Parameters here has preedence over Parameters found in the application
        config.

        :param app: Your Flask Application
        :param static_dir: The path to the ``static`` directory of your
                           application (optional)
        :param asset_dir: The path to the ``assets`` directory where Flask-Scss
                          will search ``.scss`` files (optional)
        :param load_paths: A list of folders to add to pyScss load_paths
                           (for ex., the path to a library like Compass)
        '''
        if not load_paths:
            load_paths = []

        self.app = app
        self.asset_dir = self.set_asset_dir(asset_dir)
        self.static_dir = self.set_static_dir(static_dir)
        self.assets = {}
        self.partials = {}

        load_path_list = ([self.asset_dir] if self.asset_dir else []) \
                       + (load_paths or app.config.get('SCSS_LOAD_PATHS', []))

        # pyScss.log = app.logger
        self.compiler = Compiler(search_path=load_path_list)
        if self.app.testing or self.app.debug:
            self.set_hooks()
Ejemplo n.º 10
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.º 11
0
    def parse_import_path(self, path):
        url_base = self.url_base
        url_map = self.url_map

        path = normalize_path(path)
        if path.find('!') >= 0 :
            path = path.split('!')
            prefix = path[0]
            if prefix in url_map:
                path = normalize_path(url_base + url_map[prefix] + path[1])
            else: 
                raise Exception('prefix not found ! (' + prefix + ')')    
        else:
            path = url_base + path

        if os.path.isfile(path):
            fileext = os.path.splitext(path)[1]
            if fileext == '.js' and jsx:
                with open(path) as handler:
                    content = handler.read().decode('utf-8')
                if self._match_jsx_notation(content):
                    temp = tempfile.mkstemp(fileext)
                    self._tempfile.append(temp)
                    jsx.transform(path, temp[1])
                    path = temp[1]
            if fileext == '.scss' and SCSSCompiler:
                compiler = SCSSCompiler(search_path=(self.scss_root,))
                temp = tempfile.mkstemp('.css')
                self._tempfile.append(temp)
                with open(path) as handler:
                    content = handler.read().decode('utf-8')
                content = compiler.compile_string(content)
                os.write(temp[0], content.encode('utf-8'))
                path = temp[1]

        return path
Ejemplo n.º 12
0
    def parse_import_path(self, path):
        url_base = self.url_base
        url_map = self.url_map

        path = normalize_path(path)
        if path.find('!') >= 0:
            path = path.split('!')
            prefix = path[0]
            if prefix in url_map:
                path = normalize_path(url_base + url_map[prefix] + path[1])
            else:
                raise Exception('prefix not found ! (' + prefix + ')')
        else:
            path = url_base + path

        if os.path.isfile(path):
            fileext = os.path.splitext(path)[1]
            if fileext == '.js' and jsx:
                with open(path) as handler:
                    content = handler.read()
                if self._match_jsx_notation(content):
                    temp = tempfile.mkstemp(fileext)
                    self._tempfile.append(temp)
                    jsx.transform(path, temp[1])
                    path = temp[1]
            if fileext == '.scss' and SCSSCompiler:
                compiler = SCSSCompiler(search_path=(self.scss_root, ))
                temp = tempfile.mkstemp('.css')
                self._tempfile.append(temp)
                with open(path) as handler:
                    content = handler.read()
                content = compiler.compile_string(content)
                os.write(temp[0], content.encode('utf-8'))
                path = temp[1]

        return path
Ejemplo n.º 13
0
class ScssMangler(Mangler):
    '''
    Mangle SASS/SCSS.
    '''
    def __init__(self, target, extensions=None, include_paths=None):
        super().__init__(target)
        self.extensions = extensions or ['.sass', '.scss']
        self.include_paths = include_paths or []
        self.compiler = Compiler(search_path=self.include_paths)

    def can_process(self, file_obj):
        return file_obj.current_name.suffix in self.extensions

    def process_file(self, file_obj):
        content = self.compiler.compile_string(file_obj.str)

        self.target.delete(file_obj.current_name)

        file_obj.str = content
        file_obj.current_name = file_obj.current_name.with_suffix('.css')
        yield file_obj
Ejemplo n.º 14
0
class PySCSSFilter(Filter):
    """Compiles `Scss <http://sass-lang.org/>`_ markup to real CSS.

    Requires the ``pyScss`` package (http://pypi.python.org/pypi/pyScss/).
    Run:
        $ pip install pyScss
    """

    name = 'apyscss'

    def setup(self):
        try:
            from scss.compiler import Compiler
        except ImportError:
            raise EnvironmentError('The "pyScss" package is not installed.')
        else:

            search_path = [
                path.join(path.abspath(path.dirname(__file__)), 'static', 'scss')
            ]
            self.compiler = Compiler(search_path=search_path)

    def input(self, _in, out, **kw):
        out.write(self.compiler.compile_string(_in.read()))
Ejemplo n.º 15
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.º 16
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.º 17
0
class Scss(object):
    '''
    Main and only class for Flask-Scss. It is in charge on the discovery of
    .scss files and compiles them every time they are modified.

    Any application that wants to use Flask-Scss must create a instance of this class
    '''
    def __init__(self, app, static_dir=None, asset_dir=None, load_paths=None):
        '''

        See :ref:`scss_discovery_rules`
        and :ref:`static_discovery_rules`
        for more information about the impact of ``static_dir`` and
        ``asset_dir`` parameters.

        Parameters here has preedence over Parameters found in the application
        config.

        :param app: Your Flask Application
        :param static_dir: The path to the ``static`` directory of your
                           application (optional)
        :param asset_dir: The path to the ``assets`` directory where Flask-Scss
                          will search ``.scss`` files (optional)
        :param load_paths: A list of folders to add to pyScss load_paths
                           (for ex., the path to a library like Compass)
        '''
        if not load_paths:
            load_paths = []

        self.app = app
        self.asset_dir = self.set_asset_dir(asset_dir)
        self.static_dir = self.set_static_dir(static_dir)
        self.assets = {}
        self.partials = {}

        load_path_list = ([self.asset_dir] if self.asset_dir else []) \
                       + (load_paths or app.config.get('SCSS_LOAD_PATHS', []))

        # pyScss.log = app.logger
        self.compiler = Compiler(search_path=load_path_list)
        if self.app.testing or self.app.debug:
            self.set_hooks()

    def set_asset_dir(self, asset_dir):
        asset_dir = asset_dir \
                    or self.app.config.get('SCSS_ASSET_DIR', None) \
                    or op.join(self.app.root_path, 'assets')
        if op.exists(op.join(asset_dir, 'scss')):
            return op.join(asset_dir, 'scss')
        if op.exists(asset_dir):
            return asset_dir
        return None

    def set_static_dir(self, static_dir):
        static_dir = static_dir  \
                        or self.app.config.get('SCSS_STATIC_DIR', None) \
                        or op.join(self.app.root_path, self.app.static_folder)
        if op.exists(op.join(static_dir, 'css')):
            return op.join(static_dir, 'css')
        if op.exists(static_dir):
            return static_dir
        return None

    def set_hooks(self):
        if self.asset_dir is None:
            self.app.logger.warning("The asset directory cannot be found."
                                    "Flask-Scss extension has been disabled")
            return
        if self.static_dir is None:
            self.app.logger.warning("The static directory cannot be found."
                                    "Flask-Scss extension has been disabled")
            return
        self.app.logger.info("Pyscss loaded!")
        self.app.before_request(self.update_scss)

    def discover_scss(self):
        for folder, _, files in os.walk(self.asset_dir):
            for filename in fnmatch.filter(files, '*.scss'):
                src_path = op.join(folder, filename)
                if filename.startswith('_') and src_path not in self.partials:
                    self.partials[src_path] = op.getmtime(src_path)
                elif src_path not in self.partials and src_path not in self.assets:
                    dest_path = src_path.replace(self.asset_dir,
                                                 self.static_dir).replace(
                                                     '.scss', '.css')
                    self.assets[src_path] = dest_path

    def partials_have_changed(self):
        res = False
        for partial, old_mtime in self.partials.items():
            cur_mtime = op.getmtime(partial)
            if cur_mtime > old_mtime:
                res = True
                self.partials[partial] = cur_mtime
        return res

    def update_scss(self):
        self.discover_scss()
        if self.partials_have_changed():
            for asset, dest_path in self.assets.items():
                self.compile_scss(asset, dest_path)
            return
        for asset, dest_path in self.assets.items():
            dest_mtime = op.getmtime(dest_path) \
                             if op.exists(dest_path) \
                             else -1
            if op.getmtime(asset) > dest_mtime:
                self.compile_scss(asset, dest_path)

    def compile_scss(self, asset, dest_path):
        self.app.logger.info("[flask-pyscss] refreshing %s" % (dest_path, ))
        if not os.path.exists(op.dirname(dest_path)):
            os.makedirs(op.dirname(dest_path))
        with codecs.open(dest_path, 'w', 'utf-8') as file_out:
            with open(asset) as file_in:
                file_out.write(self.compiler.compile_string(file_in.read()))
Ejemplo n.º 18
0
class Scss(object):
    '''
    Main and only class for Flask-Scss. It is in charge on the discovery of
    .scss files and compiles them every time they are modified.

    Any application that wants to use Flask-Scss must create a instance of this class
    '''

    def __init__(self, app, static_dir=None, asset_dir=None, load_paths=None):
        '''

        See :ref:`scss_discovery_rules`
        and :ref:`static_discovery_rules`
        for more information about the impact of ``static_dir`` and
        ``asset_dir`` parameters.

        Parameters here has preedence over Parameters found in the application
        config.

        :param app: Your Flask Application
        :param static_dir: The path to the ``static`` directory of your
                           application (optional)
        :param asset_dir: The path to the ``assets`` directory where Flask-Scss
                          will search ``.scss`` files (optional)
        :param load_paths: A list of folders to add to pyScss load_paths
                           (for ex., the path to a library like Compass)
        '''
        if not load_paths:
            load_paths = []

        self.app = app
        self.asset_dir = self.set_asset_dir(asset_dir)
        self.static_dir = self.set_static_dir(static_dir)
        self.assets = {}
        self.partials = {}

        load_path_list = ([self.asset_dir] if self.asset_dir else []) \
                       + (load_paths or app.config.get('SCSS_LOAD_PATHS', []))

        # pyScss.log = app.logger
        self.compiler = Compiler(search_path=load_path_list)
        if self.app.testing or self.app.debug:
            self.set_hooks()

    def set_asset_dir(self, asset_dir):
        asset_dir = asset_dir \
                    or self.app.config.get('SCSS_ASSET_DIR', None) \
                    or op.join(self.app.root_path, 'assets')
        if op.exists(op.join(asset_dir, 'scss')):
            return op.join(asset_dir, 'scss')
        if op.exists(asset_dir):
            return asset_dir
        return None

    def set_static_dir(self, static_dir):
        static_dir = static_dir  \
                        or self.app.config.get('SCSS_STATIC_DIR', None) \
                        or op.join(self.app.root_path, self.app.static_folder)
        if op.exists(op.join(static_dir, 'css')):
            return op.join(static_dir, 'css')
        if op.exists(static_dir):
            return static_dir
        return None

    def set_hooks(self):
        if self.asset_dir is None:
            self.app.logger.warning("The asset directory cannot be found."
                                    "Flask-Scss extension has been disabled")
            return
        if self.static_dir is None:
            self.app.logger.warning("The static directory cannot be found."
                                    "Flask-Scss extension has been disabled")
            return
        self.app.logger.info("Pyscss loaded!")
        self.app.before_request(self.update_scss)

    def discover_scss(self):
        for folder, _, files in os.walk(self.asset_dir):
            for filename in fnmatch.filter(files, '*.scss'):
                src_path = op.join(folder, filename)
                if filename.startswith('_') and src_path not in self.partials:
                    self.partials[src_path] = op.getmtime(src_path)
                elif src_path not in self.partials and src_path not in self.assets:
                    dest_path = src_path.replace(
                                    self.asset_dir,
                                    self.static_dir
                                ).replace('.scss', '.css')
                    self.assets[src_path] = dest_path

    def partials_have_changed(self):
        res = False
        for partial, old_mtime in self.partials.items():
            cur_mtime = op.getmtime(partial)
            if cur_mtime > old_mtime:
                res = True
                self.partials[partial] = cur_mtime
        return res

    def update_scss(self):
        self.discover_scss()
        if self.partials_have_changed():
            for asset, dest_path in self.assets.items():
                self.compile_scss(asset, dest_path)
            return
        for asset, dest_path in self.assets.items():
            dest_mtime = op.getmtime(dest_path) \
                             if op.exists(dest_path) \
                             else -1
            if op.getmtime(asset) > dest_mtime:
                self.compile_scss(asset, dest_path)

    def compile_scss(self, asset, dest_path):
        self.app.logger.info("[flask-pyscss] refreshing %s" % (dest_path,))
        if not os.path.exists(op.dirname(dest_path)):
            os.makedirs(op.dirname(dest_path))
        with codecs.open(dest_path, 'w', 'utf-8') as file_out:
            with open(asset) as file_in:
                file_out.write(self.compiler.compile_string(file_in.read()))
Ejemplo n.º 19
0
 def __init__(self, target, extensions=None, include_paths=None):
     super().__init__(target)
     self.extensions = extensions or ['.sass', '.scss']
     self.include_paths = include_paths or []
     self.compiler = Compiler(search_path=self.include_paths)
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
Archivo: legacy.py Proyecto: Alpus/Eth
    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.º 22
0
    def compile(
            self, scss_string=None, scss_file=None, source_files=None,
            super_selector=None, filename=None, is_sass=None,
            line_numbers=True):
        """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'

        # Build the compiler
        compiler = Compiler(
            namespace=root_namespace,
            search_path=search_paths,
            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),
            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,
                path=filename,
                is_sass=is_sass,
            )
            compilation.add_source(source)
        elif scss_file is not None:
            source = SourceFile.from_filename(
                scss_file,
                path=filename,
                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, path=name)
                compilation.add_source(source)

        return compiler.call_and_catch_errors(compilation.run)