diff --git a/.travis.yml b/.travis.yml index 5380f0af..a1bd3871 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python +dist: trusty python: -- pypy -- pypy3 +- pypy-5.4.1 - 2.7 - 3.4 - 3.5 diff --git a/pysass.cpp b/pysass.cpp index 51f4c8c8..7f5706a6 100644 --- a/pysass.cpp +++ b/pysass.cpp @@ -560,14 +560,14 @@ PySass_compile_filename(PyObject *self, PyObject *args) { Sass_Output_Style output_style; int source_comments, error_status, precision; PyObject *source_map_filename, *custom_functions, *custom_importers, - *result; + *result, *output_filename_hint; if (!PyArg_ParseTuple(args, - PySass_IF_PY3("yiiyiOOO", "siisiOOO"), + PySass_IF_PY3("yiiyiOOOO", "siisiOOOO"), &filename, &output_style, &source_comments, &include_paths, &precision, &source_map_filename, &custom_functions, - &custom_importers)) { + &custom_importers, &output_filename_hint)) { return NULL; } @@ -575,12 +575,17 @@ PySass_compile_filename(PyObject *self, PyObject *args) { options = sass_file_context_get_options(context); if (PyBytes_Check(source_map_filename)) { - size_t source_map_file_len = PyBytes_GET_SIZE(source_map_filename); - if (source_map_file_len) { - char *source_map_file = sass_copy_c_string( - PyBytes_AS_STRING(source_map_filename) + if (PyBytes_GET_SIZE(source_map_filename)) { + sass_option_set_source_map_file( + options, PyBytes_AS_STRING(source_map_filename) + ); + } + } + if (PyBytes_Check(output_filename_hint)) { + if (PyBytes_GET_SIZE(output_filename_hint)) { + sass_option_set_output_path( + options, PyBytes_AS_STRING(output_filename_hint) ); - sass_option_set_source_map_file(options, source_map_file); } } sass_option_set_output_style(options, output_style); diff --git a/sass.py b/sass.py index ff18885a..49a120b4 100644 --- a/sass.py +++ b/sass.py @@ -239,7 +239,7 @@ def compile_dirname( input_filename = input_filename.encode(fs_encoding) s, v, _ = _sass.compile_filename( input_filename, output_style, source_comments, include_paths, - precision, None, custom_functions, importers + precision, None, custom_functions, importers, None, ) if s: v = v.decode('UTF-8') @@ -539,30 +539,32 @@ def my_importer(path): raise TypeError('source_comments must be bool, not ' + repr(source_comments)) fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() - source_map_filename = kwargs.pop('source_map_filename', None) - if not (source_map_filename is None or - isinstance(source_map_filename, string_types)): - raise TypeError('source_map_filename must be a string, not ' + - repr(source_map_filename)) - elif isinstance(source_map_filename, text_type): - source_map_filename = source_map_filename.encode(fs_encoding) - if not ('filename' in modes or source_map_filename is None): - raise CompileError('source_map_filename is only available with ' - 'filename= keyword argument since it has to be ' - 'aware of it') - try: - include_paths = kwargs.pop('include_paths') or b'' - except KeyError: - include_paths = b'' - else: - if isinstance(include_paths, collections.Sequence): - include_paths = os.pathsep.join(include_paths) - elif not isinstance(include_paths, string_types): - raise TypeError('include_paths must be a sequence of strings, or ' - 'a colon-separated (or semicolon-separated if ' - 'Windows) string, not ' + repr(include_paths)) - if isinstance(include_paths, text_type): - include_paths = include_paths.encode(fs_encoding) + + def _get_file_arg(key): + ret = kwargs.pop(key, None) + if ret is not None and not isinstance(ret, string_types): + raise TypeError('{} must be a string, not {!r}'.format(key, ret)) + elif isinstance(ret, text_type): + ret = ret.encode(fs_encoding) + if ret and 'filename' not in modes: + raise CompileError( + '{} is only available with filename= keyword argument since ' + 'has to be aware of it'.format(key) + ) + return ret + + source_map_filename = _get_file_arg('source_map_filename') + output_filename_hint = _get_file_arg('output_filename_hint') + + include_paths = kwargs.pop('include_paths', b'') or b'' + if isinstance(include_paths, collections.Sequence): + include_paths = os.pathsep.join(include_paths) + elif not isinstance(include_paths, string_types): + raise TypeError('include_paths must be a sequence of strings, or ' + 'a colon-separated (or semicolon-separated if ' + 'Windows) string, not ' + repr(include_paths)) + if isinstance(include_paths, text_type): + include_paths = include_paths.encode(fs_encoding) custom_functions = kwargs.pop('custom_functions', ()) if isinstance(custom_functions, collections.Mapping): @@ -614,6 +616,7 @@ def my_importer(path): s, v, source_map = _sass.compile_filename( filename, output_style, source_comments, include_paths, precision, source_map_filename, custom_functions, importers, + output_filename_hint, ) if s: v = v.decode('utf-8') diff --git a/sassc.py b/sassc.py index 6de67d18..15461a8a 100755 --- a/sassc.py +++ b/sassc.py @@ -153,6 +153,7 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): output_style=options.style, source_comments=options.source_comments, source_map_filename=source_map_filename, + output_filename_hint=args[1], include_paths=options.include_paths, precision=options.precision ) diff --git a/sasstests.py b/sasstests.py index 440d448a..a50b5f5f 100644 --- a/sasstests.py +++ b/sasstests.py @@ -773,27 +773,6 @@ def test_sassc_source_map_without_css_filename(self): 'actual error message is: ' + repr(err) assert self.out.getvalue() == '' - def test_sassc_sourcemap(self): - with tempdir() as tmp_dir: - src_dir = os.path.join(tmp_dir, 'test') - shutil.copytree('test', src_dir) - src_filename = os.path.join(src_dir, 'a.scss') - out_filename = os.path.join(tmp_dir, 'a.scss.css') - exit_code = sassc.main( - ['sassc', '-m', src_filename, out_filename], - self.out, self.err - ) - assert exit_code == 0 - assert self.err.getvalue() == '' - assert self.out.getvalue() == '' - with open(out_filename) as f: - assert A_EXPECTED_CSS_WITH_MAP == f.read().strip() - with open(out_filename + '.map') as f: - self.assert_source_map_equal( - dict(A_EXPECTED_MAP, sources=None), - dict(json.load(f), sources=None) - ) - @contextlib.contextmanager def tempdir(): @@ -1399,3 +1378,32 @@ def test_stack_trace_formatting(): def test_source_comments(): out = sass.compile(string='a{color: red}', source_comments=True) assert out == '/* line 1, stdin */\na {\n color: red; }\n' + + +def test_sassc_sourcemap(tmpdir): + src_file = tmpdir.join('src').ensure_dir().join('a.scss') + out_file = tmpdir.join('a.scss.css') + out_map_file = tmpdir.join('a.scss.css.map') + + src_file.write('.c { font-size: 5px + 5px; }') + + exit_code = sassc.main([ + 'sassc', '-m', src_file.strpath, out_file.strpath, + ]) + assert exit_code == 0 + + contents = out_file.read() + assert contents == ( + '.c {\n' + ' font-size: 10px; }\n' + '\n' + '/*# sourceMappingURL=a.scss.css.map */' + ) + source_map_json = json.loads(out_map_file.read()) + assert source_map_json == { + 'sources': ['src/a.scss'], + 'version': 3, + 'names': [], + 'file': 'a.scss.css', + 'mappings': 'AAAA,AAAA,EAAE,CAAC;EAAE,SAAS,EAAE,IAAS,GAAI', + }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: