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))
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/'))))
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)
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)
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
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)
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
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)
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)
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 )
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/'))) )
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)
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
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:
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, )
class BootstrapExtension(Extension): name = 'bootstrap' namespace = Namespace()
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)
#! /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)
"""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
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
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)
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, )
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)
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)
class FontsExtension(Extension): """Functions for creating and manipulating fonts.""" name = 'fonts' namespace = Namespace()
class ExtraExtension(Extension): """Extra functions unique to the pyScss library.""" name = 'extra' namespace = Namespace()