def compile_string(self, string, filename=None): compilation = self.make_compilation() if filename is not None: f = StringIO(string) filename = PurePath(filename) source = SourceFile.from_file(f, origin=filename.parent, relpath=PurePath(filename.name)) else: source = SourceFile.from_string(string) compilation.add_source(source) return self.call_and_catch_errors(compilation.run)
def make_source_file(resource): package = resource.name.split(':')[0] relpath = resource.name.split(':')[-1] filename = relpath.split('/')[-1] source = u'\n'.join(( u'$current-package: "{0}";'.format(package), u'$current-relpath: "{0}";'.format(relpath), u'$current-filename: "{0}";'.format(filename), resource.get_source(self.context, self.request))) if ISCSSFileResource.providedBy(resource): source_file = SourceFile.from_string( source, Path(str(resource.path))) source_file.origin = Path(str(resource.path)) return source_file else: path = 'dynamic:{0}'.format(resource.name) return SourceFile.from_string(source, path)
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 handle_import(self, name, compilation, rule): """Implementation of the core Sass import mechanism, which just looks for files on disk. """ # TODO virtually all of this is the same as the django stuff, except # for the bit that actually looks for and tries to open the file. # would be much easier if you could just stick an object in the search # path that implements the pathlib API. the only problem is what to do # when one path is a child of another, so the same file has two names, # but tbh i'm not actually sure that's something worth protecting # against...? like, the only cost is that we'll parse twice (or, later # on, not respect single-import), and the fix is to just Not Do That # 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 = ['.scss', '.sass'] relative_to = path.parent basename = path.stem 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) search_path.extend(compilation.compiler.search_path) for prefix, suffix in product(('_', ''), search_exts): filename = prefix + basename + suffix for origin 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)
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)
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) search_exts = list(compilation.compiler.dynamic_extensions) if path.suffix and path.suffix in search_exts: basename = path.stem else: basename = path.name 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)
def do_build(options, args): if options.output is not None: out = open(options.output, 'wb') else: out = sys.stdout # Get the unencoded stream on Python 3 out = getattr(out, 'buffer', out) css = Scss( scss_opts={ 'style': options.style, 'debug_info': options.debug_info, }, search_paths=options.load_paths, ) if not args: args = ['-'] source_files = [] for path in args: if path == '-': source = SourceFile.from_file(sys.stdin, relpath="<stdin>", is_sass=options.is_sass) else: source = SourceFile.from_filename(path, is_sass=options.is_sass) source_files.append(source) encodings = set(source.encoding for source in source_files) if len(encodings) > 1: sys.stderr.write("Can't combine these files! " "They have different encodings: {0}\n".format( ', '.join(encodings))) sys.exit(3) output = css.compile(source_files=source_files) out.write(output.encode(source_files[0].encoding)) for f, t in profiling.items(): sys.stderr.write("%s took %03fs" % (f, t))
def compile(self, *paths): compilation = self.make_compilation() for path in paths: path = PurePath(path) if path.is_absolute(): path = path.relative_to('/') filename, storage = get_file_and_storage(str(path)) with storage.open(filename) as f: source = SourceFile.from_file(f, origin=path.parent, relpath=PurePath(path.name)) compilation.add_source(source) return self.call_and_catch_errors(compilation.run)
def do_build(options, args): if options.output is not None: out = open(options.output, 'wb') else: out = sys.stdout # Get the unencoded stream on Python 3 out = getattr(out, 'buffer', out) css = Scss(scss_opts={ 'style': options.style, 'debug_info': options.debug_info, }, search_paths=options.load_paths, ) if not args: args = ['-'] source_files = [] for path in args: if path == '-': source = SourceFile.from_file(sys.stdin, relpath="<stdin>", is_sass=options.is_sass) else: source = SourceFile.from_filename(path, is_sass=options.is_sass) source_files.append(source) encodings = set(source.encoding for source in source_files) if len(encodings) > 1: sys.stderr.write( "Can't combine these files! " "They have different encodings: {0}\n" .format(', '.join(encodings)) ) sys.exit(3) output = css.compile(source_files=source_files) out.write(output.encode(source_files[0].encoding)) for f, t in profiling.items(): sys.stderr.write("%s took %03fs" % (f, t))
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 handle_import(self, name, compilation, rule): """ Re-implementation of the core Sass import mechanism, which looks for files using the staticfiles storage and staticfiles finders. """ original_path = PurePath(name) search_exts = list(compilation.compiler.dynamic_extensions) if original_path.suffix and original_path.suffix in search_exts: basename = original_path.stem else: basename = original_path.name if original_path.is_absolute() or name[ 0] == '/': # such that name like '/path/to/file.scss' works on Windows # Remove the beginning slash search_path = original_path.relative_to('/').parent elif rule.source_file.origin: search_path = rule.source_file.origin if original_path.parent: search_path = os.path.normpath( str(search_path / original_path.parent)) else: search_path = original_path.parent for prefix, suffix in product(('_', ''), search_exts): filename = PurePath(prefix + basename + suffix) full_filename, storage = get_file_and_storage( str(search_path / filename)) if full_filename: with storage.open(full_filename) as f: return SourceFile.from_file(f, origin=search_path, relpath=filename)
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)
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)
def compile_scss_string(self, source, debug=False): source_file = SourceFile.from_string(source) return self._compile((source_file,), debug=debug)
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)
def handle_import(self, name, compilation, rule): return SourceFile.from_string( '', relpath=name )
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)
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)
def handle_import(self, name, compilation, rule): """ Re-implementation of the core Sass import mechanism, which looks for files using the staticfiles storage and staticfiles finders. """ original_path = PurePath(name) if original_path.suffix: search_exts = [original_path.suffix] else: search_exts = compilation.compiler.dynamic_extensions if original_path.is_absolute(): # Remove the beginning slash search_path = original_path.relative_to('/').parent elif rule.source_file.origin: search_path = rule.source_file.origin if original_path.parent: search_path = search_path / original_path.parent else: search_path = original_path.parent basename = original_path.stem for prefix, suffix in product(('_', ''), search_exts): filename = PurePath(prefix + basename + suffix) full_filename, storage = get_file_and_storage(str(search_path / filename)) if full_filename: with storage.open(full_filename) as f: return SourceFile.from_file(f, origin=search_path, relpath=filename)