Added CompassFilter.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,4 +10,5 @@ MANIFEST
|
||||
*.egg
|
||||
docs/_build/
|
||||
.coverage
|
||||
htmlcov
|
||||
htmlcov
|
||||
.sass-cache
|
||||
@@ -130,7 +130,7 @@ class Compressor(object):
|
||||
"mimetype '%s'." % mimetype)
|
||||
else:
|
||||
return CompilerFilter(content, filter_type=self.type,
|
||||
command=command, filename=filename).output(**kwargs)
|
||||
command=command, filename=filename).input(**kwargs)
|
||||
return content
|
||||
|
||||
def filter(self, content, method, **kwargs):
|
||||
|
||||
@@ -40,6 +40,10 @@ class CompressorSettings(AppSettings):
|
||||
YUI_JS_ARGUMENTS = ''
|
||||
DATA_URI_MIN_SIZE = 1024
|
||||
|
||||
COMPASS_BINARY = 'compass'
|
||||
COMPASS_ARGUMENTS = ' --no-line-comments --output-style expanded'
|
||||
COMPASS_PLUGINS = []
|
||||
|
||||
# the cache backend to use
|
||||
CACHE_BACKEND = None
|
||||
# rebuilds the cache every 30 days if nothing has changed.
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import os
|
||||
import logging
|
||||
import subprocess
|
||||
import tempfile
|
||||
from django.utils.datastructures import SortedDict
|
||||
|
||||
from compressor.conf import settings
|
||||
from compressor.exceptions import FilterError
|
||||
from compressor.utils import cmd_split, stringformat
|
||||
from compressor.utils import cmd_split
|
||||
from compressor.utils.stringformat import FormattableString as fstr
|
||||
|
||||
logger = logging.getLogger("compressor.filters")
|
||||
|
||||
|
||||
class FilterBase(object):
|
||||
|
||||
def __init__(self, content, filter_type=None, verbose=0):
|
||||
def __init__(self, content, filter_type=None, filename=None, verbose=0):
|
||||
self.type = filter_type
|
||||
self.content = content
|
||||
self.verbose = verbose or settings.COMPRESS_VERBOSE
|
||||
self.logger = logger
|
||||
self.filename = filename
|
||||
|
||||
def input(self, **kwargs):
|
||||
raise NotImplementedError
|
||||
@@ -30,40 +34,53 @@ class CompilerFilter(FilterBase):
|
||||
external commands.
|
||||
"""
|
||||
command = None
|
||||
filename = None
|
||||
options = {}
|
||||
options = ()
|
||||
|
||||
def __init__(self, content, command=None, filename=None, *args, **kwargs):
|
||||
def __init__(self, content, command=None, *args, **kwargs):
|
||||
super(CompilerFilter, self).__init__(content, *args, **kwargs)
|
||||
self.cwd = None
|
||||
if command:
|
||||
self.command = command
|
||||
if self.command is None:
|
||||
raise FilterError("Required attribute 'command' not given")
|
||||
self.filename = filename
|
||||
if isinstance(self.options, dict):
|
||||
new_options = ()
|
||||
for item in kwargs.iteritems():
|
||||
new_options += (item,)
|
||||
self.options = new_options
|
||||
for item in kwargs.iteritems():
|
||||
self.options += (item,)
|
||||
self.stdout = subprocess.PIPE
|
||||
self.stdin = subprocess.PIPE
|
||||
self.stderr = subprocess.PIPE
|
||||
self.infile, self.outfile = None, None
|
||||
|
||||
def output(self, **kwargs):
|
||||
infile = None
|
||||
outfile = None
|
||||
try:
|
||||
def input(self, **kwargs):
|
||||
options = dict(self.options)
|
||||
if self.infile is None:
|
||||
if "{infile}" in self.command:
|
||||
infile = tempfile.NamedTemporaryFile(mode='w')
|
||||
infile.write(self.content)
|
||||
infile.flush()
|
||||
self.options["infile"] = self.filename or infile.name
|
||||
if "{outfile}" in self.command:
|
||||
ext = ".%s" % self.type and self.type or ""
|
||||
outfile = tempfile.NamedTemporaryFile(mode='rw', suffix=ext)
|
||||
self.options["outfile"] = outfile.name
|
||||
command = stringformat.FormattableString(self.command)
|
||||
proc = subprocess.Popen(cmd_split(command.format(**self.options)),
|
||||
stdout=self.stdout, stdin=self.stdin, stderr=self.stderr)
|
||||
if infile is not None:
|
||||
filtered, err = proc.communicate()
|
||||
else:
|
||||
if self.filename is None:
|
||||
self.infile = tempfile.NamedTemporaryFile(mode="w")
|
||||
self.infile.write(self.content)
|
||||
self.infile.flush()
|
||||
os.fsync(self.infile)
|
||||
options["infile"] = self.infile.name
|
||||
else:
|
||||
self.infile = open(self.filename)
|
||||
options["infile"] = self.filename
|
||||
|
||||
if "{outfile}" in self.command and not "outfile" in options:
|
||||
ext = ".%s" % self.type and self.type or ""
|
||||
self.outfile = tempfile.NamedTemporaryFile(mode='r+', suffix=ext)
|
||||
options["outfile"] = self.outfile.name
|
||||
try:
|
||||
command = fstr(self.command).format(**options)
|
||||
proc = subprocess.Popen(cmd_split(command), shell=os.name=='nt',
|
||||
stdout=self.stdout, stdin=self.stdin, stderr=self.stderr, cwd=self.cwd)
|
||||
if self.infile is None:
|
||||
filtered, err = proc.communicate(self.content)
|
||||
else:
|
||||
filtered, err = proc.communicate()
|
||||
except (IOError, OSError), e:
|
||||
raise FilterError('Unable to apply %s (%r): %s' %
|
||||
(self.__class__.__name__, self.command, e))
|
||||
@@ -75,11 +92,13 @@ class CompilerFilter(FilterBase):
|
||||
raise FilterError(err)
|
||||
if self.verbose:
|
||||
self.logger.debug(err)
|
||||
if outfile is not None:
|
||||
filtered = outfile.read()
|
||||
outfile_path = options.get('outfile')
|
||||
if outfile_path:
|
||||
self.outfile = open(outfile_path, 'r')
|
||||
finally:
|
||||
if infile is not None:
|
||||
infile.close()
|
||||
if outfile is not None:
|
||||
outfile.close()
|
||||
if self.infile is not None:
|
||||
self.infile.close()
|
||||
if self.outfile is not None:
|
||||
filtered = self.outfile.read()
|
||||
self.outfile.close()
|
||||
return filtered
|
||||
|
||||
@@ -4,7 +4,7 @@ from compressor.filters import CompilerFilter
|
||||
|
||||
class ClosureCompilerFilter(CompilerFilter):
|
||||
command = "{binary} {args}"
|
||||
options = {
|
||||
"binary": settings.COMPRESS_CLOSURE_COMPILER_BINARY,
|
||||
"args": settings.COMPRESS_CLOSURE_COMPILER_ARGUMENTS,
|
||||
}
|
||||
options = (
|
||||
("binary", settings.COMPRESS_CLOSURE_COMPILER_BINARY),
|
||||
("args", settings.COMPRESS_CLOSURE_COMPILER_ARGUMENTS),
|
||||
)
|
||||
|
||||
37
compressor/filters/compass.py
Normal file
37
compressor/filters/compass.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import tempfile
|
||||
from os import path
|
||||
|
||||
from compressor.conf import settings
|
||||
from compressor.filters import CompilerFilter
|
||||
|
||||
|
||||
class CompassFilter(CompilerFilter):
|
||||
"""
|
||||
Converts Compass files to css.
|
||||
"""
|
||||
command = "{binary} compile --force --quiet --boring {args} "
|
||||
options = (
|
||||
("binary", settings.COMPRESS_COMPASS_BINARY),
|
||||
("args", settings.COMPRESS_COMPASS_ARGUMENTS),
|
||||
)
|
||||
|
||||
def input(self, *args, **kwargs):
|
||||
if self.filename is None:
|
||||
self.filename = kwargs.pop('filename')
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
parentdir = path.abspath(path.dirname(self.filename))
|
||||
self.cwd = path.dirname(parentdir)
|
||||
self.infile = open(self.filename)
|
||||
outfile_name = path.splitext(path.split(self.filename)[1])[0] + '.css'
|
||||
self.options += (
|
||||
('infile', self.filename),
|
||||
('tmpdir', tmpdir),
|
||||
('sassdir', parentdir),
|
||||
('outfile', path.join(tmpdir, outfile_name)),
|
||||
('imagedir', settings.COMPRESS_URL),
|
||||
)
|
||||
for plugin in settings.COMPRESS_COMPASS_PLUGINS:
|
||||
self.command += ' --require %s'% plugin
|
||||
self.command += (' --sass-dir {sassdir} --css-dir {tmpdir}'
|
||||
' --image-dir {imagedir} {infile}')
|
||||
return super(CompassFilter, self).input(*args, **kwargs)
|
||||
@@ -4,7 +4,7 @@ from compressor.filters import CompilerFilter
|
||||
|
||||
class CSSTidyFilter(CompilerFilter):
|
||||
command = "{binary} {infile} {args} {outfile}"
|
||||
options = {
|
||||
"binary": settings.COMPRESS_CSSTIDY_BINARY,
|
||||
"args": settings.COMPRESS_CSSTIDY_ARGUMENTS,
|
||||
}
|
||||
options = (
|
||||
("binary", settings.COMPRESS_CSSTIDY_BINARY),
|
||||
("args", settings.COMPRESS_CSSTIDY_ARGUMENTS),
|
||||
)
|
||||
|
||||
@@ -14,15 +14,15 @@ class YUICompressorFilter(CompilerFilter):
|
||||
|
||||
class YUICSSFilter(YUICompressorFilter):
|
||||
type = 'css'
|
||||
options = {
|
||||
"binary": settings.COMPRESS_YUI_BINARY,
|
||||
"args": settings.COMPRESS_YUI_CSS_ARGUMENTS,
|
||||
}
|
||||
options = (
|
||||
("binary", settings.COMPRESS_YUI_BINARY),
|
||||
("args", settings.COMPRESS_YUI_CSS_ARGUMENTS),
|
||||
)
|
||||
|
||||
|
||||
class YUIJSFilter(YUICompressorFilter):
|
||||
type = 'js'
|
||||
options = {
|
||||
"binary": settings.COMPRESS_YUI_BINARY,
|
||||
"args": settings.COMPRESS_YUI_JS_ARGUMENTS,
|
||||
}
|
||||
options = (
|
||||
("binary", settings.COMPRESS_YUI_BINARY),
|
||||
("args", settings.COMPRESS_YUI_JS_ARGUMENTS),
|
||||
)
|
||||
|
||||
@@ -71,6 +71,7 @@ class CompressorNode(template.Node):
|
||||
return cache_content
|
||||
|
||||
# 4. call compressor output method and handle exceptions
|
||||
rendered_output = compressor.output(self.mode, forced=forced)
|
||||
try:
|
||||
rendered_output = compressor.output(self.mode, forced=forced)
|
||||
if cache_key:
|
||||
|
||||
24
compressor/tests/media/config.rb
Normal file
24
compressor/tests/media/config.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "stylesheets"
|
||||
sass_dir = "sass"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascripts"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
|
||||
5
compressor/tests/media/sass/ie.scss
Normal file
5
compressor/tests/media/sass/ie.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Welcome to Compass. Use this file to write IE specific override styles.
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <!--[if IE]>
|
||||
* <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
|
||||
* <![endif]--> */
|
||||
3
compressor/tests/media/sass/print.scss
Normal file
3
compressor/tests/media/sass/print.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
/* Welcome to Compass. Use this file to define print styles.
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
|
||||
6
compressor/tests/media/sass/screen.scss
Normal file
6
compressor/tests/media/sass/screen.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Welcome to Compass.
|
||||
* In this file you should write your main styles. (or centralize your imports)
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */
|
||||
|
||||
@import "compass/reset";
|
||||
5
compressor/tests/media/stylesheets/ie.css
Normal file
5
compressor/tests/media/stylesheets/ie.css
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Welcome to Compass. Use this file to write IE specific override styles.
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <!--[if IE]>
|
||||
* <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
|
||||
* <![endif]--> */
|
||||
3
compressor/tests/media/stylesheets/print.css
Normal file
3
compressor/tests/media/stylesheets/print.css
Normal file
@@ -0,0 +1,3 @@
|
||||
/* Welcome to Compass. Use this file to define print styles.
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
|
||||
69
compressor/tests/media/stylesheets/screen.css
Normal file
69
compressor/tests/media/stylesheets/screen.css
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Welcome to Compass.
|
||||
* In this file you should write your main styles. (or centralize your imports)
|
||||
* Import this file using the following HTML or equivalent:
|
||||
* <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */
|
||||
/* line 17, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* line 20, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* line 22, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* line 24, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* line 26, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
caption, th, td {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* line 28, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
q, blockquote {
|
||||
quotes: none;
|
||||
}
|
||||
/* line 101, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
q:before, q:after, blockquote:before, blockquote:after {
|
||||
content: "";
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* line 30, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* line 115, ../../../../../usr/local/Cellar/gems/1.8/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
@@ -23,9 +23,8 @@ def main():
|
||||
content = content.replace('background:', 'color:')
|
||||
|
||||
if options.outfile:
|
||||
f = open(options.outfile, 'w')
|
||||
f.write(content)
|
||||
f.close()
|
||||
with open(options.outfile, 'w') as f:
|
||||
f.write(content)
|
||||
else:
|
||||
print content
|
||||
|
||||
|
||||
@@ -483,43 +483,81 @@ color: black;
|
||||
"""
|
||||
from compressor.filters.csstidy import CSSTidyFilter
|
||||
self.assertEqual(
|
||||
"font,th,td,p{color:#000;}", CSSTidyFilter(content).output())
|
||||
"font,th,td,p{color:#000;}", CSSTidyFilter(content).input())
|
||||
|
||||
CssTidyTestCase = skipIf(
|
||||
find_command(settings.COMPRESS_CSSTIDY_BINARY) is None,
|
||||
'CSStidy binary %r not found' % settings.COMPRESS_CSSTIDY_BINARY
|
||||
)(CssTidyTestCase)
|
||||
|
||||
|
||||
class CompassTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.old_debug = settings.DEBUG
|
||||
self.old_compress_css_filters = settings.COMPRESS_CSS_FILTERS
|
||||
self.old_compress_url = settings.COMPRESS_URL
|
||||
self.old_enabled = settings.COMPRESS_ENABLED
|
||||
settings.DEBUG = True
|
||||
settings.COMPRESS_ENABLED = True
|
||||
settings.COMPRESS_CSS_FILTERS = [
|
||||
'compressor.filters.compass.CompassFilter',
|
||||
'compressor.filters.css_default.CssAbsoluteFilter',
|
||||
]
|
||||
settings.COMPRESS_URL = '/media/'
|
||||
|
||||
def tearDown(self):
|
||||
settings.DEBUG = self.old_debug
|
||||
settings.COMPRESS_URL = self.old_compress_url
|
||||
settings.COMPRESS_ENABLED = self.old_enabled
|
||||
settings.COMPRESS_CSS_FILTERS = self.old_compress_css_filters
|
||||
|
||||
def test_compass(self):
|
||||
template = u"""{% load compress %}{% compress css %}
|
||||
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/screen.scss" type="text/css" charset="utf-8">
|
||||
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/print.scss" type="text/css" charset="utf-8">
|
||||
{% endcompress %}
|
||||
"""
|
||||
context = {'MEDIA_URL': settings.COMPRESS_URL}
|
||||
out = u'<link rel="stylesheet" href="/media/CACHE/css/3f807af2259c.css" type="text/css">'
|
||||
self.assertEqual(out, render(template, context))
|
||||
|
||||
CompassTestCase = skipIf(
|
||||
find_command(settings.COMPRESS_COMPASS_BINARY) is None,
|
||||
'Compass binary %r not found' % settings.COMPRESS_COMPASS_BINARY
|
||||
)(CompassTestCase)
|
||||
|
||||
|
||||
class PrecompilerTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.this_dir = os.path.dirname(__file__)
|
||||
self.filename = os.path.join(self.this_dir, 'media/css/one.css')
|
||||
self.test_precompiler = os.path.join(self.this_dir, 'precompiler.py')
|
||||
with open(self.filename) as f:
|
||||
self.content = f.read()
|
||||
self.test_precompiler = os.path.join(self.this_dir, 'precompiler.py')
|
||||
|
||||
def test_precompiler_infile_outfile(self):
|
||||
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
|
||||
self.assertEqual(u"body { color:#990; }", compiler.output())
|
||||
|
||||
def test_precompiler_stdin_outfile(self):
|
||||
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=None, command=command)
|
||||
self.assertEqual(u"body { color:#990; }", compiler.output())
|
||||
|
||||
def test_precompiler_stdin_stdout(self):
|
||||
command = '%s %s' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=None, command=command)
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.output())
|
||||
|
||||
def test_precompiler_stdin_stdout_filename(self):
|
||||
command = '%s %s' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.output())
|
||||
self.assertEqual(u"body { color:#990; }", compiler.input())
|
||||
|
||||
def test_precompiler_infile_stdout(self):
|
||||
command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=None, command=command)
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.output())
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.input())
|
||||
|
||||
def test_precompiler_stdin_outfile(self):
|
||||
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=None, command=command)
|
||||
self.assertEqual(u"body { color:#990; }", compiler.input())
|
||||
|
||||
def test_precompiler_stdin_stdout(self):
|
||||
command = '%s %s' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=None, command=command)
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.input())
|
||||
|
||||
def test_precompiler_stdin_stdout_filename(self):
|
||||
command = '%s %s' % (sys.executable, self.test_precompiler)
|
||||
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
|
||||
self.assertEqual(u"body { color:#990; }\n", compiler.input())
|
||||
|
||||
Reference in New Issue
Block a user