def input(self, _in, out, **kwargs): source = kwargs.get('source_path') # We need an input file for the compiler to work if source is None: raise FilterError('ember-precompile: no input file') # Setup the command line executable proc = subprocess.Popen(['ember-precompile', source], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # Execute the shell command stdout, stderr = proc.communicate() if proc.returncode != 0: message = ("ember-precompile: error in subprocess stderr=%s, stdout=%s, " "returncode=%s" % (stderr, stdout, proc.returncode)) raise FilterError(message) elif stderr: print('ember-precompile filter has warnings:', stderr) out.write(stdout.decode('utf-8'))
def output(self, _in, out, **kw): binary = self.coffee_bin or self.coffee_deprecated or 'coffee' if self.coffee_deprecated: import warnings warnings.warn( 'The COFFEE_PATH option of the "coffeescript" ' + 'filter has been deprecated and will be removed.' + 'Use COFFEE_BIN instead.', ImminentDeprecationWarning) args = "-sp" + ("" if self.no_bare else 'b') try: proc = subprocess.Popen([binary, args], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as e: if e.errno == 2: raise Exception( "coffeescript not installed or in system path for webassets" ) raise stdout, stderr = proc.communicate(_in.read().encode('utf-8')) if proc.returncode != 0: raise FilterError( ('coffeescript: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) elif stderr: print("coffeescript filter has warnings:", stderr) out.write(stdout.decode('utf-8'))
def input(self, _in, out, **kwargs): """ Compile individual Jade templates """ proc = subprocess.Popen(self.argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=(os.name == 'nt')) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError(('jade: subprocess returned a non-success ' + 'result code: %s, stdout=%s, stderr=%s') % (proc.returncode, stdout, stderr)) elif stderr: print('jade filter has warnings:', stderr) # Add a bit of JavaScript that will place our compiled Jade function # into an object on the `window` object. Jade files are keyed by their # basename. key = os.path.splitext(os.path.basename(kwargs['source_path']))[0] preamble = "window['%s']['%s'] = " % (self.js_var, key) out.write('%s%s' % (preamble, stdout.strip()))
def process_templates(self, out, hunks, **kw): templates = [info['source_path'] for _, info in hunks] if self.root is True: root = self.get_config('directory') elif self.root: root = path.join(self.get_config('directory'), self.root) else: root = self._find_base_path(templates) args = [self.binary or 'handlebars'] if root: args.extend(['-r', root]) if self.extra_args: args.extend(self.extra_args) args.extend(templates) proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError( ('handlebars: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) out.write(stdout.strip() + ';')
def output(self, _in, out, **kw): # The typescript compiler cannot read a file which does not have # the .ts extension. The output file needs to have an extension, # or the compiler will want to create a directory in its place. input_filename = tempfile.mktemp() + ".ts" output_filename = tempfile.mktemp() + ".js" with open(input_filename, 'w') as f: f.write(_in.read()) args = [self.binary or 'tsc', '--out', output_filename, input_filename] if self.config: args += self.config.split() proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=(os.name == 'nt')) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError("typescript: subprocess had error: stderr=%s," % stderr + "stdout=%s, returncode=%s" % (stdout, proc.returncode)) with open(output_filename, 'r') as f: out.write(f.read()) os.unlink(input_filename) os.unlink(output_filename)
def output(self, _in, out, **kw): # The typescript compiler cannot read a file which does not have # the .ts extension input_filename = tempfile.mktemp() + ".ts" output_filename = tempfile.mktemp() with open(input_filename, 'wb') as f: f.write(_in.read()) args = [self.binary or 'tsc', '--out', output_filename, input_filename] proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError("typescript: subprocess had error: stderr=%s," % stderr + "stdout=%s, returncode=%s" % (stdout, proc.returncode)) with open(output_filename, 'rb') as f: out.write(f.read()) os.unlink(input_filename) os.unlink(output_filename)
def _apply_sass(self, _in, out, cd=None): # Switch to source file directory if asked, so that this directory # is by default on the load path. We could pass it via -I, but then # files in the (undefined) wd could shadow the correct files. if cd: old_dir = os.getcwd() os.chdir(cd) # Put together the load path. load_paths = self.load_paths or [] if self.includes_dir: load_paths.append(self.includes_dir) import warnings warnings.warn( 'The INCLUDES_DIR option of the "sass" filter has ' 'been deprecated and will be removed. Use LOAD_PATHS' 'instead.', ImminentDeprecationWarning) try: args = [self.binary or 'sass', '--stdin', '--style', 'expanded', '--line-comments'] if isinstance(self.env.cache, FilesystemCache): args.extend(['--cache-location', os.path.join(self.env.cache.directory, 'sass')]) if (self.env.debug if self.debug_info is None else self.debug_info): args.append('--debug-info') if self.use_scss: args.append('--scss') if self.use_compass: args.append('--compass') for path in load_paths: args.extend(['-I', path]) for lib in self.libs or []: args.extend(['-r', lib]) proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError(('sass: subprocess had error: stderr=%s, '+ 'stdout=%s, returncode=%s') % ( stderr, stdout, proc.returncode)) elif stderr: print "sass filter has warnings:", stderr out.write(stdout) finally: if cd: os.chdir(old_dir)
def _apply_sass(self, _in, out, cd=None): # Switch to source file directory if asked, so that this directory # is by default on the load path. We could pass it via --include-paths, but then # files in the (undefined) wd could shadow the correct files. from tempfile import TemporaryFile old_dir = os.getcwd() if cd: os.chdir(cd) with TemporaryFile() as stdin, TemporaryFile( ) as stdout, TemporaryFile() as stderr: try: args = [ self.binary or 'node-sass', '--output-style', self.style or 'expanded' ] if not self.use_scss: args.append("--indented-syntax") if (self.ctx.environment.debug if self.debug_info is None else self.debug_info): args.append('--debug-info') for path in self.load_paths or []: args.extend(['--include-path', path]) if (self.cli_args): args.extend(self.cli_args) stdin.write(_in.read().encode('utf-8')) stdin.seek(0) proc = subprocess.Popen( args, stdin=stdin, stdout=stdout, stderr=stderr, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) proc.wait() stdout.seek(0) stderr.seek(0) if proc.returncode != 0: raise FilterError( 'sass: subprocess had error: stderr={}, stdout={}, returncode={}' .format(stderr.read(), stdout.read(), proc.returncode)) elif stderr: print("node-sass filter has warnings:", stderr) out.write(stdout.read().decode('utf-8')) finally: if cd: os.chdir(old_dir)
def _apply_sass(self, _in, out, cd=None): # Switch to source file directory if asked, so that this directory # is by default on the load path. We could pass it via -I, but then # files in the (undefined) wd could shadow the correct files. orig_cwd = os.getcwd() child_cwd = orig_cwd if cd: child_cwd = cd args = [ self.binary or 'sass', '--stdin', '--style', self.style or 'expanded' ] if self.line_comments is None or self.line_comments: args.append('--line-comments') if isinstance(self.ctx.cache, FilesystemCache): args.extend([ '--cache-location', os.path.join(orig_cwd, self.ctx.cache.directory, 'sass') ]) elif not cd: # Without a fixed working directory, the location of the cache # is basically undefined, so prefer not to use one at all. args.extend(['--no-cache']) if (self.ctx.environment.debug if self.debug_info is None else self.debug_info): args.append('--debug-info') if self.use_scss: args.append('--scss') if self.use_compass: args.append('--compass') for path in self.load_paths or []: args.extend(['-I', path]) for lib in self.libs or []: args.extend(['-r', lib]) proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt'), cwd=child_cwd) stdout, stderr = proc.communicate(_in.read().encode('utf-8')) if proc.returncode != 0: raise FilterError(('sass: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) elif stderr: print("sass filter has warnings:", stderr) out.write(stdout.decode('utf-8'))
def input(self, in_, out, **kwargs): h = connector.Handler(self.env, in_, out, self.plugins if self.plugins else {}, self.imports if self.imports else [], kwargs["source"]) h.vendor_path = self.vendor_path if not self.compass: raise FilterError("Compass bin path is not set") h.start(self.compass) self.depends = h.deps
def run_input_optimizer(source_path, output_path, **kwargs): """ Run Optimizer Command and return file """ MB_MEMORY_FOR_JAVA = 256 if not source_path.endswith(".js"): raise FilterError('requireJS: bundle file is not a js-file') base_url = kwargs.get('base_url', './') use_rhino = kwargs.get('use_rhino', False) # Use the provided name or calculate the name relative to the base_url and minus the ".js"-part working_dir = os.getcwd() relpath = os.path.relpath(source_path, working_dir) name = kwargs.get('name', os.path.relpath(relpath, base_url)[:-3]) # Any extra args that should be provided to r.js extra_arg_string = "" extra_args = kwargs.get("extra_args", {}) for key, value in extra_args.iteritems(): extra_arg_string += "%s=%s " % (key, value) compiler_cmd = '' if use_rhino: compiler_cmd = "java -Xmx{memory}m -classpath {rhino_js_jar}:{closure_compiler_jar} " \ "org.mozilla.javascript.tools.shell.Main {require_js} " \ "-o baseUrl={base_url} name={name} out={require_output_path} {require_extra_args}".format(memory=MB_MEMORY_FOR_JAVA, rhino_js_jar=RequireOptimizer._resource_path("js.jar"), closure_compiler_jar=RequireOptimizer._resource_path("compiler.jar"), require_js=RequireOptimizer._resource_path("r.js"), base_url=base_url, name=name, require_output_path=output_path, require_extra_args=extra_arg_string) else: compiler_cmd = 'node {require_js} -o baseUrl={base_url} name={name} ' \ 'out={require_output_path} {require_extra_args}'.format(require_js=RequireOptimizer._resource_path("r.js"), base_url=base_url, name=name, require_output_path=output_path, require_extra_args=extra_arg_string) # Run the compiler. try: try: os.system(compiler_cmd) except OSError: print "Unable to execute {0}, is it installed?".format('java' if use_rhino else 'node') fo = open(output_path, "r+") return fo.read() except: return ''
def output(self, _in, out, **kw): proc = subprocess.Popen([self.binary or 'cleancss'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError(('cleancss: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) out.write(stdout)
def java_run(self, args): proc = subprocess.Popen( [self.java, '-jar', self.jar] + args, # we cannot use the in/out streams directly, as they might be # StringIO objects (which are not supported by subprocess) stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode: raise FilterError( '%s: subprocess returned a ' 'non-success result code: %s, stdout=%s, stderr=%s' % (self.name, proc.returncode, stdout, stderr))
def output(self, _in, out, **kw): args = ['iced', '-sp', '--runtime', 'inline'] proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate(_in.read().encode('utf-8')) if proc.returncode != 0: raise FilterError( ( 'iced: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s' ) % (stderr, stdout, proc.returncode) ) elif stderr: print("coffeescript filter has warnings:", stderr) out.write(stdout.decode('utf-8'))
def output(self, _in, out, **kw): args = [self.binary] if self.extra_args: args.extend(self.extra_args) proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError(('uglifyjs: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) out.write(stdout)
def open(self, out, source_path, **kw): proc = subprocess.Popen( [self.less or 'lessc', source_path], stdout = subprocess.PIPE, stderr = subprocess.PIPE ) stdout, stderr = proc.communicate() # At the moment (2011-12-09), there's a bug in the current version of # Less that always prints an error to stdout so the returncode is the # only way of determining if Less is actually having a compilation # error. if proc.returncode != 0: raise FilterError(('less: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % ( stderr, stdout, proc.returncode)) out.write(stdout)
def input(self, infile, outfile, **kwargs): args = ['browserify'] transforms = ['babelify'] for transform in transforms: args.extend(('--transform', transform)) # Use the resolvify module to include npm modules from the # vendor directory. args.extend( ('--transform', '[resolvify', 'vendor', 'vendor/node_modules]')) args.append(kwargs['source_path']) if app.config['DEBUG']: args.append('--debug') try: self.subprocess(args, outfile, infile) except FilterError as e: raise FilterError(str(e).replace('\\n', '\n'))
def _apply_sass(self, _in, out, includes_path): if includes_path: old_dir = os.getcwd() os.chdir(includes_path) try: args = [ self.binary or 'sass', '--stdin', '--style', 'expanded', '--line-comments' ] if isinstance(self.env.cache, FilesystemCache): args.extend([ '--cache-location', os.path.join(self.env.cache.directory, 'sass') ]) if (self.env.debug if self.debug_info is None else self.debug_info): args.append('--debug-info') if self.use_scss: args.append('--scss') if self.use_compass: args.append('--compass') proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError(('sass: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) elif stderr: print "sass filter has warnings:", stderr out.write(stdout) finally: if includes_path: os.chdir(old_dir)
def input(self, _in, out, source_path, output_path): old_dir = os.getcwd() os.chdir(os.path.dirname(source_path)) try: args = "-p" + ("" if self.no_bare else 'b') proc = subprocess.Popen([self.coffee, args, source_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError( ('coffeescript: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) elif stderr: print "coffeescript filter has warnings:", stderr out.write(stdout) finally: os.chdir(old_dir)
def open(self, out, source_path, **kw): args = [] if self.node_path: args += [self.node_path] args += [self.dusty_path or 'dusty'] # no need for --single, as we output to STDOUT args += [source_path] proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError(('dusty: subprocess had error: stderr=%s,' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) out.write(stdout)
def input(self, _in, out, **kwargs): input_filename = kwargs['source_path'] output_filename = tempfile.mktemp() + ".js" proc = subprocess.Popen( self.get_executable_list(input_filename, output_filename), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError( 'babel: subprocess had error: stderr={},' 'stdout={}, returncode={}'.format( stderr, stdout, proc.returncode)) with open(output_filename, 'r') as f: out.write(f.read()) os.unlink(output_filename)
def input(self, _in, out, source_path, output_path): proc = subprocess.Popen( [self.less or 'lessc'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) with open(source_path) as src: stdout, stderr = proc.communicate(input=src.read()) # less only writes to stdout, as noted in the method doc, but # check everything anyway. if stderr or proc.returncode != 0: raise FilterError(('less: subprocess had error: stderr=%s, ' 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) out.write(stdout)
def input(self, in_, out, source_path, **kw): # Set working directory to the source file so that includes are found with working_directory(filename=source_path): proc = subprocess.Popen( [self.less or 'lessc', '-'], stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE ) stdout, stderr = proc.communicate(in_.read()) # At the moment (2011-12-09), there's a bug in the current version of # Less that always prints an error to stdout so the returncode is the # only way of determining if Less is actually having a compilation # error. if proc.returncode != 0: raise FilterError(('less: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % ( stderr, stdout, proc.returncode)) out.write(stdout)
def open(self, out, sourcePath, **kw): """Less currently doesn't take data from stdin, and doesn't allow us from stdout either. Neither does it return a proper non-0 error code when an error occurs, or even write to stderr (stdout instead)! Hopefully this will improve in the future: http://groups.google.com/group/lesscss/browse_thread/thread/3aed033a44c51b4c/b713148afde87e81 """ # TODO: Use NamedTemporaryFile. outtemp_name = os.path.join(tempfile.gettempdir(), 'assets_temp_%d.css' % int(time.time())) proc = subprocess.Popen( [self.less or 'lessc', sourcePath, outtemp_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) stdout, stderr = proc.communicate() # less only writes to stdout, as noted in the method doc, but # check everything anyway. if stdout or stderr or proc.returncode != 0: if os.path.exists(outtemp_name): os.unlink(outtemp_name) raise FilterError(('less: subprocess had error: stderr=%s, '+ 'stdout=%s, returncode=%s') % ( stderr, stdout, proc.returncode)) outtemp = open(outtemp_name) try: out.write(outtemp.read()) finally: outtemp.close() os.unlink(outtemp_name)
def input(self, _in, out, **kwargs): input_filename = tempfile.mktemp() + "-babel.js" output_filename = tempfile.mktemp() + ".js" with open(input_filename, 'w') as f: f.write(_in.getvalue()) proc = subprocess.Popen(self.get_executable_list( input_filename, output_filename), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if proc.returncode != 0: raise FilterError('babel: subprocess had error: stderr=%s,' 'stdout=%s, returncode=%s'.format( stderr, stdout, proc.returncode)) with open(output_filename, 'r') as f: out.write(f.read()) os.unlink(input_filename) os.unlink(output_filename)
def output(self, _in, out, **kw): binary = self.coffee_bin or self.coffee_deprecated or 'coffee' if self.coffee_deprecated: import warnings warnings.warn( 'The COFFEE_PATH option of the "coffeescript" ' + 'filter has been deprecated and will be removed.' + 'Use COFFEE_BIN instead.', ImminentDeprecationWarning) args = "-sp" + ("" if self.no_bare else 'b') proc = subprocess.Popen([binary, args], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate(_in.read()) if proc.returncode != 0: raise FilterError( ('coffeescript: subprocess had error: stderr=%s, ' + 'stdout=%s, returncode=%s') % (stderr, stdout, proc.returncode)) elif stderr: print "coffeescript filter has warnings:", stderr out.write(stdout)
def open(self, out, source_path, **kw): """Compass currently doesn't take data from stdin, and doesn't allow us accessing the result from stdout either. Also, there's a bunch of other issues we need to work around: - compass doesn't support given an explict output file, only a "--css-dir" output directory. We have to "guess" the filename that will be created in that directory. - The output filename used is based on the input filename, and simply cutting of the length of the "sass_dir" (and changing the file extension). That is, compass expects the input filename to always be inside the "sass_dir" (which defaults to ./src), and if this is not the case, the output filename will be gibberish (missing characters in front). See: https://github.com/chriseppstein/compass/issues/304 We fix this by setting the proper --sass-dir option. - Compass insists on creating a .sass-cache folder in the current working directory, and unlike the sass executable, there doesn't seem to be a way to disable it. The workaround is to set the working directory to our temp directory, so that the cache folder will be deleted at the end. """ # Create temp folder one dir below output_path so sources in # sourcemap are correct. This will be in the project folder, # and as such, while exteremly unlikely, this could interfere # with existing files and directories. tempout_dir = path.normpath( path.join(path.dirname(kw['output_path']), '../') ) tempout = tempfile.mkdtemp(dir=tempout_dir) # Temporarily move to "tempout", so .sass-cache will be created there old_wd = os.getcwd() os.chdir(tempout) try: # Make sure to use normpath() to not cause trouble with # compass' simplistic path handling, where it just assumes # source_path is within sassdir, and cuts off the length of # sassdir from the input file. sassdir = path.normpath(path.dirname(source_path)) source_path = path.normpath(source_path) # Compass offers some helpers like image-url(), which need # information about the urls under which media files will be # available. This is hard for two reasons: First, the options in # question aren't supported on the command line, so we need to write # a temporary config file. Secondly, they assume defined and # separate directories for "images", "stylesheets" etc., something # webassets knows nothing of: we don't support the user defining # such directories. Because we traditionally had this # filter point all type-specific directories to the root media # directory, we will define the paths to match this. In other # words, in Compass, both inline-image("img/test.png) and # image-url("img/test.png") will find the same file, and assume it # to be {env.directory}/img/test.png. # However, this partly negates the purpose of an utility like # image-url() in the first place - you not having to hard code # the location of your images. So we allow direct modification of # the configuration file via the COMPASS_CONFIG setting (see # tickets #36 and #125). # # Note that there is also the --relative-assets option, which we # can't use because it calculates an actual relative path between # the image and the css output file, the latter being in a # temporary directory in our case. config = CompassConfig( project_path=self.ctx.directory, http_path=self.ctx.url, http_images_dir='', http_stylesheets_dir='', http_fonts_dir='', http_javascripts_dir='', images_dir='', output_style=':expanded', ) # Update with the custom config dictionary, if any. if self.config: config.update(self.config) config_file = path.join(tempout, '.config.rb') f = open(config_file, 'w') try: f.write(config.to_string()) f.flush() finally: f.close() command = [self.compass or 'compass', 'compile'] for plugin in self.plugins or []: command.extend(('--require', plugin)) command.extend(['--sass-dir', sassdir, '--css-dir', tempout, '--config', config_file, '--quiet', '--boring', source_path]) proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell: necessary on windows to execute # ruby files, but doesn't work on linux. shell=(os.name == 'nt')) stdout, stderr = proc.communicate() # compass seems to always write a utf8 header? to stderr, so # make sure to not fail just because there's something there. if proc.returncode != 0: raise FilterError(('compass: subprocess had error: stderr=%s, '+ 'stdout=%s, returncode=%s') % ( stderr, stdout, proc.returncode)) guessed_outputfilename = path.splitext(path.basename(source_path))[0] guessed_outputfilepath = path.join(tempout, guessed_outputfilename) output_file = open("%s.css" % guessed_outputfilepath, encoding='utf-8') if config.get('sourcemap'): sourcemap_file = open("%s.css.map" % guessed_outputfilepath) sourcemap_output_filepath = path.join( path.dirname(kw['output_path']), path.basename(sourcemap_file.name) ) if not path.exists(path.dirname(sourcemap_output_filepath)): os.mkdir(path.dirname(sourcemap_output_filepath)) sourcemap_output_file = open(sourcemap_output_filepath, 'w') sourcemap_output_file.write(sourcemap_file.read()) sourcemap_file.close() try: contents = output_file.read() out.write(contents) finally: output_file.close() finally: # Restore previous working dir os.chdir(old_wd) # Clean up the temp dir shutil.rmtree(tempout)
def subprocess(cls, argv, out, data=None): """Execute the commandline given by the list in ``argv``. If a byestring is given via ``data``, it is piped into data. ``argv`` may contain two placeholders: ``{input}`` If given, ``data`` will be written to a temporary file instead of data. The placeholder is then replaced with that file. ``{output}`` Will be replaced by a temporary filename. The return value then will be the content of this file, rather than stdout. """ class tempfile_on_demand(object): def __repr__(self): if not hasattr(self, 'filename'): self.fd, self.filename = tempfile.mkstemp() return self.filename @property def created(self): return hasattr(self, 'filename') # Replace input and output placeholders input_file = tempfile_on_demand() output_file = tempfile_on_demand() if hasattr(str, 'format'): # Support Python 2.5 without the feature argv = list( map( lambda item: item.format(input=input_file, output=output_file), argv)) try: data = (data.read() if hasattr(data, 'read') else data) if data is not None: data = data.encode('utf-8') if input_file.created: if not data: raise ValueError( '{input} placeholder given, but no data passed') with os.fdopen(input_file.fd, 'wb') as f: f.write(data) # No longer pass to stdin data = None proc = subprocess.Popen( argv, # we cannot use the in/out streams directly, as they might be # StringIO objects (which are not supported by subprocess) stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, shell=os.name == 'nt') stdout, stderr = proc.communicate(data) if proc.returncode: raise FilterError( '%s: subprocess returned a non-success result code: ' '%s, stdout=%s, stderr=%s' % (cls.name or cls.__name__, proc.returncode, stdout, stderr)) else: if output_file.created: with open(output_file.filename, 'rb') as f: out.write(f.read().decode('utf-8')) else: out.write(stdout.decode('utf-8')) finally: if output_file.created: os.unlink(output_file.filename) if input_file.created: os.unlink(input_file.filename)