From de226c7487e24b8e33720cea9bc6ed1254c32aee Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 23 Aug 2018 09:47:53 -0700 Subject: [PATCH] Format / lint via pre-commit --- .pre-commit-config.yaml | 18 +++ .travis.yml | 3 +- Makefile | 1 - build_manylinux_wheels.py | 2 +- docs/changes.rst | 4 +- docs/conf.py | 34 ++++-- requirements-dev.txt | 2 +- sass.py | 66 ++++++----- sassc.py | 48 +++++--- sasstests.py | 239 +++++++++++++++++++++++--------------- sassutils/builder.py | 41 ++++--- sassutils/distutils.py | 8 +- sassutils/wsgi.py | 18 +-- setup.py | 45 ++++--- testpkg/setup.py | 4 +- upload_appveyor_builds.py | 42 ++++--- 16 files changed, 353 insertions(+), 222 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..b28d758b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v1.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - id: flake8 + exclude: ^docs/conf.py +- repo: https://github.com/asottile/pyupgrade + rev: v1.4.0 + hooks: + - id: pyupgrade +- repo: https://github.com/asottile/add-trailing-comma + rev: v0.6.4 + hooks: + - id: add-trailing-comma diff --git a/.travis.yml b/.travis.yml index a2107847..5c1aa2d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,10 @@ script: - COVERAGE_PROCESS_START=$PWD/.coveragerc pytest sasstests.py - coverage combine - coverage report -- flake8 . +- pre-commit run --all-files --show-diff-on-failure after_success: - coveralls cache: directories: - $HOME/.cache/pip + - $HOME/.cache/pre-commit diff --git a/Makefile b/Makefile index 4a4db520..3db2ec9f 100644 --- a/Makefile +++ b/Makefile @@ -41,4 +41,3 @@ _sass.so: $(C_OBJECTS) $(CPP_OBJECTS) build2/pysass.o .PHONY: clean clean: rm -rf build2 _sass.so - diff --git a/build_manylinux_wheels.py b/build_manylinux_wheels.py index bc7c9c6f..829c660d 100755 --- a/build_manylinux_wheels.py +++ b/build_manylinux_wheels.py @@ -41,7 +41,7 @@ def main(): 'quay.io/pypa/manylinux1_x86_64:latest', 'bash', '-exc', '{} wheel --verbose --wheel-dir /work --no-deps libsass && ' - 'auditwheel repair --wheel-dir /dist /work/*.whl'.format(pip) + 'auditwheel repair --wheel-dir /dist /work/*.whl'.format(pip), ) dists = tuple(os.path.join('dist', p) for p in os.listdir('dist')) return upload.main(('-r', 'pypi', '--skip-existing') + dists) diff --git a/docs/changes.rst b/docs/changes.rst index da393310..3e76df68 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -471,7 +471,7 @@ or Visual Studio 2013 Update 4+. - Follow up the libsass upstream: 3.0 --- See the `release note`__ of LibSass. - Decent extends support - - Basic Sass Maps Support + - Basic Sass Maps Support - Better UTF-8 Support - ``call()`` function - Better Windows Support @@ -602,7 +602,7 @@ Released on February 21, 2014. exist yet, rather than siliently fails. [:issue:`8`, :issue:`9` by Philipp Volguine] - Merged recent changes from libsass 1.0.1: `57a2f62--v1.0.1`_. - + - Supports `variable arguments`_. - Supports sourcemaps. diff --git a/docs/conf.py b/docs/conf.py index daf7a40d..45771bb9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,8 +30,10 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', - 'sphinx.ext.extlinks'] +extensions = [ + 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', + 'sphinx.ext.extlinks', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -191,8 +193,10 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'libsass.tex', u'libsass Documentation', - u'Hong Minhee', 'manual'), + ( + 'index', 'libsass.tex', u'libsass Documentation', + u'Hong Minhee', 'manual', + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -221,8 +225,10 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'libsass', u'libsass Documentation', - [u'Hong Minhee'], 1) + ( + 'index', 'libsass', u'libsass Documentation', + [u'Hong Minhee'], 1, + ), ] # If true, show URL addresses after external links. @@ -235,9 +241,11 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'libsass', u'libsass Documentation', - u'Hong Minhee', 'libsass', 'One line description of project.', - 'Miscellaneous'), + ( + 'index', 'libsass', u'libsass Documentation', + u'Hong Minhee', 'libsass', 'One line description of project.', + 'Miscellaneous', + ), ] # Documents to append as an appendix to all manuals. @@ -254,14 +262,16 @@ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'setuptools': ('https://setuptools.readthedocs.io/', None), - 'flask': ('http://flask.pocoo.org/docs/', None) + 'flask': ('http://flask.pocoo.org/docs/', None), } extlinks = { 'issue': ('https://github.com/sass/libsass-python/issues/%s', '#'), - 'branch': ('https://github.com/sass/libsass-python/compare/master...%s', - ''), + 'branch': ( + 'https://github.com/sass/libsass-python/compare/master...%s', + '', + ), 'commit': ('https://github.com/sass/libsass-python/commit/%s', ''), 'upcommit': ('https://github.com/sass/libsass/commit/%s', ''), } diff --git a/requirements-dev.txt b/requirements-dev.txt index 11f0e993..9e1601cc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ -e . coverage coverage-enable-subprocess -flake8>=2.4.0 +pre-commit pytest werkzeug>=0.9 diff --git a/sass.py b/sass.py index f38a53aa..3dd040f6 100644 --- a/sass.py +++ b/sass.py @@ -110,17 +110,19 @@ def from_lambda(cls, name, lambda_): if PY2: # pragma: no cover a = inspect.getargspec(lambda_) varargs, varkw, defaults, kwonlyargs = ( - a.varargs, a.keywords, a.defaults, None) + a.varargs, a.keywords, a.defaults, None, + ) else: # pragma: no cover a = inspect.getfullargspec(lambda_) varargs, varkw, defaults, kwonlyargs = ( - a.varargs, a.varkw, a.defaults, a.kwonlyargs) + a.varargs, a.varkw, a.defaults, a.kwonlyargs, + ) if varargs or varkw or defaults or kwonlyargs: raise TypeError( - 'functions cannot have starargs or defaults: {0} {1}'.format( - name, lambda_ - ) + 'functions cannot have starargs or defaults: {} {}'.format( + name, lambda_, + ), ) return cls(name, a.args, lambda_) @@ -157,7 +159,7 @@ def __init__(self, name, arguments, callable_): @property def signature(self): """Signature string of the function.""" - return '{0}({1})'.format(self.name, ', '.join(self.arguments)) + return '{}({})'.format(self.name, ', '.join(self.arguments)) def __call__(self, *args, **kwargs): return self.callable_(*args, **kwargs) @@ -177,7 +179,7 @@ def _to_importer_result(single_result): if len(single_result) not in (1, 2, 3): raise ValueError( 'Expected importer result to be a tuple of length (1, 2, 3) ' - 'but got {0}: {1!r}'.format(len(single_result), single_result) + 'but got {}: {!r}'.format(len(single_result), single_result), ) def _to_bytes(obj): @@ -222,7 +224,7 @@ def _raise(e): def compile_dirname( search_path, output_path, output_style, source_comments, include_paths, - precision, custom_functions, importers, custom_import_extensions + precision, custom_functions, importers, custom_import_extensions, ): fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() for dirpath, _, filenames in os.walk(search_path, onerror=_raise): @@ -257,10 +259,10 @@ def compile_dirname( def _check_no_remaining_kwargs(func, kwargs): if kwargs: raise TypeError( - '{0}() got unexpected keyword argument(s) {1}'.format( + '{}() got unexpected keyword argument(s) {}'.format( func.__name__, - ', '.join("'{0}'".format(arg) for arg in sorted(kwargs)), - ) + ', '.join("'{}'".format(arg) for arg in sorted(kwargs)), + ), ) @@ -511,8 +513,10 @@ def my_importer(path): if not modes: raise TypeError('choose one at least in ' + and_join(MODES)) elif len(modes) > 1: - raise TypeError(and_join(modes) + ' are exclusive each other; ' - 'cannot be used at a time') + raise TypeError( + and_join(modes) + ' are exclusive each other; ' + 'cannot be used at a time', + ) precision = kwargs.pop('precision', 5) output_style = kwargs.pop('output_style', 'nested') if not isinstance(output_style, string_types): @@ -521,13 +525,15 @@ def my_importer(path): try: output_style = OUTPUT_STYLES[output_style] except KeyError: - raise CompileError('{0} is unsupported output_style; choose one of {1}' + raise CompileError('{} is unsupported output_style; choose one of {}' ''.format(output_style, and_join(OUTPUT_STYLES))) source_comments = kwargs.pop('source_comments', False) if source_comments in SOURCE_COMMENTS: if source_comments == 'none': - deprecation_message = ('you can simply pass False to ' - "source_comments instead of 'none'") + deprecation_message = ( + 'you can simply pass False to ' + "source_comments instead of 'none'" + ) source_comments = False elif source_comments in ('line_numbers', 'default'): deprecation_message = ('you can simply pass True to ' @@ -535,15 +541,17 @@ def my_importer(path): repr(source_comments)) source_comments = True else: - deprecation_message = ("you don't have to pass 'map' to " - 'source_comments but just need to ' - 'specify source_map_filename') + deprecation_message = ( + "you don't have to pass 'map' to " + 'source_comments but just need to ' + 'specify source_map_filename' + ) source_comments = False warnings.warn( "values like 'none', 'line_numbers', and 'map' for " 'the source_comments parameter are deprecated; ' + deprecation_message, - DeprecationWarning + DeprecationWarning, ) if not isinstance(source_comments, bool): raise TypeError('source_comments must be bool, not ' + @@ -559,7 +567,7 @@ def _get_file_arg(key): 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) + 'has to be aware of it'.format(key), ) return ret @@ -591,14 +599,14 @@ def _get_file_arg(key): '- a set/sequence of {0.__module__}.{0.__name__} objects,\n' '- a mapping of function name strings to lambda functions,\n' '- a set/sequence of named functions,\n' - 'not {1!r}'.format(SassFunction, custom_functions) + 'not {1!r}'.format(SassFunction, custom_functions), ) _custom_exts = kwargs.pop('custom_import_extensions', []) or [] if not isinstance(_custom_exts, (list, tuple)): raise TypeError( 'custom_import_extensions must be a list of strings ' - 'not {}'.format(type(_custom_exts)) + 'not {}'.format(type(_custom_exts)), ) custom_import_extensions = [ext.encode('utf-8') for ext in _custom_exts] @@ -624,7 +632,7 @@ def _get_file_arg(key): if not isinstance(filename, string_types): raise TypeError('filename must be a string, not ' + repr(filename)) elif not os.path.isfile(filename): - raise IOError('{0!r} seems not a file'.format(filename)) + raise IOError('{!r} seems not a file'.format(filename)) elif isinstance(filename, text_type): filename = filename.encode(fs_encoding) _check_no_remaining_kwargs(compile, kwargs) @@ -643,13 +651,15 @@ def _get_file_arg(key): try: search_path, output_path = kwargs.pop('dirname') except ValueError: - raise ValueError('dirname must be a pair of (source_dir, ' - 'output_dir)') + raise ValueError( + 'dirname must be a pair of (source_dir, ' + 'output_dir)', + ) _check_no_remaining_kwargs(compile, kwargs) s, v = compile_dirname( search_path, output_path, output_style, source_comments, include_paths, precision, custom_functions, importers, - custom_import_extensions + custom_import_extensions, ) if s: return @@ -777,7 +787,7 @@ def __len__(self): # Our interface def __repr__(self): - return '{0}({1})'.format(type(self).__name__, frozenset(self.items())) + return '{}({})'.format(type(self).__name__, frozenset(self.items())) def __hash__(self): return self._hash diff --git a/sassc.py b/sassc.py index ac8fa150..469c83dc 100755 --- a/sassc.py +++ b/sassc.py @@ -71,7 +71,7 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): parser = optparse.OptionParser( usage='%prog [options] SCSS_FILE [OUT_CSS_FILE]', - version='%prog {0} (sass/libsass {1})'.format( + version='%prog {} (sass/libsass {})'.format( sass.__version__, sass.libsass_version, ), ) @@ -85,30 +85,38 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): output_styles + '. [default: %default]' ), ) - parser.add_option('-m', '-g', '--sourcemap', dest='source_map', - action='store_true', default=False, - help='Emit source map. Requires the second argument ' - '(output css filename).') - parser.add_option('-I', '--include-path', metavar='DIR', - dest='include_paths', action='append', - help='Path to find "@import"ed (S)CSS source files. ' - 'Can be multiply used.') + parser.add_option( + '-m', '-g', '--sourcemap', dest='source_map', + action='store_true', default=False, + help='Emit source map. Requires the second argument ' + '(output css filename).', + ) + parser.add_option( + '-I', '--include-path', metavar='DIR', + dest='include_paths', action='append', + help='Path to find "@import"ed (S)CSS source files. ' + 'Can be multiply used.', + ) parser.add_option( '-p', '--precision', action='store', type='int', default=5, - help='Set the precision for numbers. [default: %default]' + help='Set the precision for numbers. [default: %default]', ) parser.add_option( '--source-comments', action='store_true', default=False, help='Include debug info in output', ) - parser.add_option('--import-extensions', - dest='custom_import_extensions', action='append', - help='Extra extensions allowed for sass imports. ' - 'Can be multiply used.') + parser.add_option( + '--import-extensions', + dest='custom_import_extensions', action='append', + help='Extra extensions allowed for sass imports. ' + 'Can be multiply used.', + ) options, args = parser.parse_args(argv[1:]) - error = functools.partial(print, - parser.get_prog_name() + ': error:', - file=stderr) + error = functools.partial( + print, + parser.get_prog_name() + ': error:', + file=stderr, + ) if not args: parser.print_usage(stderr) error('too few arguments') @@ -120,8 +128,10 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): filename = args[0] if options.source_map and len(args) < 2: parser.print_usage(stderr) - error('-m/-g/--sourcemap requires the second argument, the output ' - 'css filename.') + error( + '-m/-g/--sourcemap requires the second argument, the output ' + 'css filename.', + ) return 2 try: diff --git a/sasstests.py b/sasstests.py index eac8360c..ff55396e 100644 --- a/sasstests.py +++ b/sasstests.py @@ -141,7 +141,7 @@ def assert_source_map_file(self, expected, filename): tree = json.load(f) except ValueError as e: # pragma: no cover f.seek(0) - msg = '{0!s}\n\n{1}:\n\n{2}'.format(e, filename, f.read()) + msg = '{!s}\n\n{}:\n\n{}'.format(e, filename, f.read()) raise ValueError(msg) self.assert_source_map_equal(expected, tree) @@ -158,11 +158,11 @@ def test_output_styles(self): def test_and_join(self): self.assertEqual( 'Korea, Japan, China, and Taiwan', - sass.and_join(['Korea', 'Japan', 'China', 'Taiwan']) + sass.and_join(['Korea', 'Japan', 'China', 'Taiwan']), ) self.assertEqual( 'Korea, and Japan', - sass.and_join(['Korea', 'Japan']) + sass.and_join(['Korea', 'Japan']), ) assert 'Korea' == sass.and_join(['Korea']) assert '' == sass.and_join([]) @@ -177,31 +177,49 @@ def test_compile_takes_only_keywords(self): self.assertRaises(TypeError, sass.compile, 'a { color: blue; }') def test_compile_exclusive_arguments(self): - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', filename='test/a.scss') - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', dirname='test/') - self.assertRaises(TypeError, sass.compile, - filename='test/a.scss', dirname='test/') + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', filename='test/a.scss', + ) + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', dirname='test/', + ) + self.assertRaises( + TypeError, sass.compile, + filename='test/a.scss', dirname='test/', + ) def test_compile_invalid_output_style(self): - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', - output_style=['compact']) - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', output_style=123j) - self.assertRaises(ValueError, sass.compile, - string='a { color: blue; }', output_style='invalid') + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', + output_style=['compact'], + ) + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', output_style=123j, + ) + self.assertRaises( + ValueError, sass.compile, + string='a { color: blue; }', output_style='invalid', + ) def test_compile_invalid_source_comments(self): - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', - source_comments=['line_numbers']) - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', source_comments=123j) - self.assertRaises(TypeError, sass.compile, - string='a { color: blue; }', - source_comments='invalid') + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', + source_comments=['line_numbers'], + ) + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', source_comments=123j, + ) + self.assertRaises( + TypeError, sass.compile, + string='a { color: blue; }', + source_comments='invalid', + ) def test_compile_disallows_arbitrary_arguments(self): for args in ( @@ -219,10 +237,12 @@ def test_compile_disallows_arbitrary_arguments(self): def test_compile_string(self): actual = sass.compile(string='a { b { color: blue; } }') assert actual == 'a b {\n color: blue; }\n' - commented = sass.compile(string='''a { + commented = sass.compile( + string='''a { b { color: blue; } color: red; - }''', source_comments=True) + }''', source_comments=True, + ) assert commented == '''/* line 1, stdin */ a { color: red; } @@ -238,19 +258,25 @@ def test_compile_string(self): /* 유니코드 */ ''', - actual + actual, + ) + self.assertRaises( + sass.CompileError, sass.compile, + string='a { b { color: blue; }', ) - self.assertRaises(sass.CompileError, sass.compile, - string='a { b { color: blue; }') # sass.CompileError should be a subtype of ValueError - self.assertRaises(ValueError, sass.compile, - string='a { b { color: blue; }') + self.assertRaises( + ValueError, sass.compile, + string='a { b { color: blue; }', + ) self.assertRaises(TypeError, sass.compile, string=1234) self.assertRaises(TypeError, sass.compile, string=[]) def test_compile_string_sass_style(self): - actual = sass.compile(string='a\n\tb\n\t\tcolor: blue;', - indented=True) + actual = sass.compile( + string='a\n\tb\n\t\tcolor: blue;', + indented=True, + ) assert actual == 'a b {\n color: blue; }\n' def test_importer_one_arg(self): @@ -322,7 +348,7 @@ def importer_with_srcmap(path): json.dumps({ "version": 3, "sources": [ - path + ".db" + path + ".db", ], "mappings": ";AAAA,CAAC,CAAC;EAAE,KAAK,EAAE,GAAI,GAAI", }), @@ -340,7 +366,7 @@ def importer_with_srcmap(path): def test_importers_raises_exception(self): def importer(path): - raise ValueError('Bad path: {0}'.format(path)) + raise ValueError('Bad path: {}'.format(path)) with assert_raises_compile_error(RegexMatcher( r'^Error: \n' @@ -349,7 +375,7 @@ def importer(path): r'ValueError: Bad path: hi\n' r' on line 1 of stdin\n' r'>> @import "hi";\n' - r' --------\^\n' + r' --------\^\n', )): sass.compile(string='@import "hi";', importers=((0, importer),)) @@ -365,7 +391,7 @@ def importer(path): r'length \(1, 2, 3\) but got 0: \(\)\n' r' on line 1 of stdin\n' r'>> @import "hi";\n' - r' --------\^\n' + r' --------\^\n', )): sass.compile(string='@import "hi";', importers=((0, importer),)) @@ -381,7 +407,7 @@ def importer(path): r"length \(1, 2, 3\) but got 4: \('a', 'b', 'c', 'd'\)\n" r' on line 1 of stdin\n' r'>> @import "hi";\n' - r' --------\^\n' + r' --------\^\n', )): sass.compile(string='@import "hi";', importers=((0, importer),)) @@ -393,8 +419,10 @@ def test_compile_string_deprecated_source_comments_line_numbers(self): expected = sass.compile(string=source, source_comments=True) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') - actual = sass.compile(string=source, - source_comments='line_numbers') + actual = sass.compile( + string=source, + source_comments='line_numbers', + ) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) assert expected == actual @@ -408,8 +436,10 @@ def test_compile_filename(self): assert D_EXPECTED_CSS == actual actual = sass.compile(filename='test/e.scss') assert actual == E_EXPECTED_CSS - self.assertRaises(IOError, sass.compile, - filename='test/not-exist.sass') + self.assertRaises( + IOError, sass.compile, + filename='test/not-exist.sass', + ) self.assertRaises(TypeError, sass.compile, filename=1234) self.assertRaises(TypeError, sass.compile, filename=[]) @@ -417,7 +447,7 @@ def test_compile_source_map(self): filename = 'test/a.scss' actual, source_map = sass.compile( filename=filename, - source_map_filename='a.scss.css.map' + source_map_filename='a.scss.css.map', ) assert A_EXPECTED_CSS_WITH_MAP == actual self.assert_source_map_equal(A_EXPECTED_MAP, source_map) @@ -426,14 +456,14 @@ def test_compile_source_map_deprecated_source_comments_map(self): filename = 'test/a.scss' expected, expected_map = sass.compile( filename=filename, - source_map_filename='a.scss.css.map' + source_map_filename='a.scss.css.map', ) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') actual, actual_map = sass.compile( filename=filename, source_comments='map', - source_map_filename='a.scss.css.map' + source_map_filename='a.scss.css.map', ) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) @@ -447,23 +477,27 @@ def test_compile_with_precision(self): assert actual == G_EXPECTED_CSS_WITH_PRECISION_8 def test_regression_issue_2(self): - actual = sass.compile(string=''' + actual = sass.compile( + string=''' @media (min-width: 980px) { a { color: red; } } - ''') + ''', + ) normalized = re.sub(r'\s+', '', actual) assert normalized == '@media(min-width:980px){a{color:red;}}' def test_regression_issue_11(self): - actual = sass.compile(string=''' + actual = sass.compile( + string=''' $foo: 3; @media (max-width: $foo) { body { color: black; } } - ''') + ''', + ) normalized = re.sub(r'\s+', '', actual) assert normalized == '@media(max-width:3){body{color:black;}}' @@ -515,7 +549,7 @@ def test_builder_build_directory(self): assert E_EXPECTED_CSS == css self.assertEqual( os.path.join('subdir', 'recur.scss.css'), - result_files[os.path.join('subdir', 'recur.scss')] + result_files[os.path.join('subdir', 'recur.scss')], ) with io.open( os.path.join(css_path, 'g.scss.css'), encoding='UTF-8', @@ -524,7 +558,7 @@ def test_builder_build_directory(self): assert G_EXPECTED_CSS == css self.assertEqual( os.path.join('subdir', 'recur.scss.css'), - result_files[os.path.join('subdir', 'recur.scss')] + result_files[os.path.join('subdir', 'recur.scss')], ) with io.open( os.path.join(css_path, 'subdir', 'recur.scss.css'), @@ -535,16 +569,20 @@ def test_builder_build_directory(self): def test_output_style(self): css_path = self.css_path - result_files = build_directory(self.sass_path, css_path, - output_style='compressed') + result_files = build_directory( + self.sass_path, css_path, + output_style='compressed', + ) assert len(result_files) == 7 assert 'a.scss.css' == result_files['a.scss'] with io.open( os.path.join(css_path, 'a.scss.css'), encoding='UTF-8', ) as f: css = f.read() - self.assertEqual('body{background-color:green}body a{color:blue}\n', - css) + self.assertEqual( + 'body{background-color:green}body a{color:blue}\n', + css, + ) class ManifestTestCase(BaseTestCase): @@ -609,7 +647,7 @@ def replace_source_path(s, name): 'GAChB' ), }, - os.path.join(d, 'css', 'b.scss.css.map') + os.path.join(d, 'css', 'b.scss.css.map'), ) m.build_one(d, 'd.scss', source_map=True) with io.open( @@ -631,7 +669,7 @@ def replace_source_path(s, name): 'GAC7B' ), }, - os.path.join(d, 'css', 'd.scss.css.map') + os.path.join(d, 'css', 'd.scss.css.map'), ) @@ -666,9 +704,11 @@ def test_wsgi_sass_middleware(self): with tempdir() as css_dir: src_dir = os.path.join(css_dir, 'src') shutil.copytree('test', src_dir) - app = SassMiddleware(self.sample_wsgi_app, { - __name__: (src_dir, css_dir, '/static') - }) + app = SassMiddleware( + self.sample_wsgi_app, { + __name__: (src_dir, css_dir, '/static'), + }, + ) client = Client(app, Response) r = client.get('/asdf') assert r.status_code == 200 @@ -707,7 +747,7 @@ def build_sass(self, *args): testpkg_path = os.path.join(os.path.dirname(__file__), 'testpkg') return subprocess.call( [sys.executable, 'setup.py', 'build_sass'] + list(args), - cwd=os.path.abspath(testpkg_path) + cwd=os.path.abspath(testpkg_path), ) def test_build_sass(self): @@ -715,12 +755,12 @@ def test_build_sass(self): assert rv == 0 self.assertEqual( ['a.scss.css'], - list(map(os.path.basename, self.list_built_css())) + list(map(os.path.basename, self.list_built_css())), ) with open(self.css_path('a.scss.css')) as f: self.assertEqual( 'p a {\n color: red; }\n\np b {\n color: blue; }\n', - f.read() + f.read(), ) def test_output_style(self): @@ -729,7 +769,7 @@ def test_output_style(self): with open(self.css_path('a.scss.css')) as f: self.assertEqual( 'p a{color:red}p b{color:blue}\n', - f.read() + f.read(), ) @@ -750,7 +790,7 @@ def test_no_args(self): def test_three_args(self): exit_code = sassc.main( ['sassc', 'a.scss', 'b.scss', 'c.scss'], - self.out, self.err + self.out, self.err, ) assert exit_code == 2 err = self.err.getvalue() @@ -768,8 +808,10 @@ def test_sassc_output(self): fd, tmp = tempfile.mkstemp('.css') try: os.close(fd) - exit_code = sassc.main(['sassc', 'test/a.scss', tmp], - self.out, self.err) + exit_code = sassc.main( + ['sassc', 'test/a.scss', tmp], + self.out, self.err, + ) assert exit_code == 0 assert self.err.getvalue() == '' assert self.out.getvalue() == '' @@ -782,8 +824,10 @@ def test_sassc_output_unicode(self): fd, tmp = tempfile.mkstemp('.css') try: os.close(fd) - exit_code = sassc.main(['sassc', 'test/d.scss', tmp], - self.out, self.err) + exit_code = sassc.main( + ['sassc', 'test/d.scss', tmp], + self.out, self.err, + ) assert exit_code == 0 assert self.err.getvalue() == '' assert self.out.getvalue() == '' @@ -796,9 +840,11 @@ def test_sassc_source_map_without_css_filename(self): exit_code = sassc.main(['sassc', '-m', 'a.scss'], self.out, self.err) assert exit_code == 2 err = self.err.getvalue() - assert err.strip().endswith('error: -m/-g/--sourcemap requires ' - 'the second argument, the output css ' - 'filename.'), \ + assert err.strip().endswith( + 'error: -m/-g/--sourcemap requires ' + 'the second argument, the output css ' + 'filename.', + ), \ 'actual error message is: ' + repr(err) assert self.out.getvalue() == '' @@ -828,10 +874,14 @@ def test_successful(self): input_dir = os.path.join(tmpdir, 'input') output_dir = os.path.join(tmpdir, 'output') os.makedirs(os.path.join(input_dir, 'foo')) - write_file(os.path.join(input_dir, 'f1.scss'), - 'a { b { width: 100%; } }') - write_file(os.path.join(input_dir, 'foo/f2.scss'), - 'foo { width: 100%; }') + write_file( + os.path.join(input_dir, 'f1.scss'), + 'a { b { width: 100%; } }', + ) + write_file( + os.path.join(input_dir, 'foo/f2.scss'), + 'foo { width: 100%; }', + ) # Make sure we don't compile non-scss files write_file(os.path.join(input_dir, 'baz.txt'), 'Hello der') @@ -881,7 +931,7 @@ def test_error(self): with pytest.raises(sass.CompileError) as excinfo: sass.compile( - dirname=(input_dir, os.path.join(tmpdir, 'output')) + dirname=(input_dir, os.path.join(tmpdir, 'output')), ) msg, = excinfo.value.args assert msg.startswith('Error: Invalid CSS after ') @@ -890,10 +940,7 @@ def test_error(self): class SassFunctionTest(unittest.TestCase): def test_from_lambda(self): - # Hack for https://gitlab.com/pycqa/flake8/issues/117 - def noop(x): - return x - lambda_ = noop(lambda abc, d: None) # pragma: no branch (lambda) + lambda_ = lambda abc, d: None # pragma: no branch # noqa: E731 sf = sass.SassFunction.from_lambda('func_name', lambda_) assert 'func_name' == sf.name assert ('$abc', '$d') == sf.arguments @@ -909,7 +956,7 @@ def test_sigature(self): sf = sass.SassFunction( # pragma: no branch (doesn't run lambda) 'func-name', ('$a', '$bc', '$d'), - lambda a, bc, d: None + lambda a, bc, d: None, ) assert 'func-name($a, $bc, $d)' == sf.signature assert sf.signature == str(sf) @@ -1161,7 +1208,7 @@ def test_raises(self): r' on line 1 of stdin, in function `raises`\n' r' from line 1 of stdin\n' r'>> a { content: raises\(\); }\n' - r' -------------\^\n$' + r' -------------\^\n$', )): compile_with_func('a { content: raises(); }') @@ -1172,7 +1219,7 @@ def test_warning(self): ' on line 1 of stdin, in function `returns_warning`\n' ' from line 1 of stdin\n' '>> a { content: returns_warning(); }\n' - ' -------------^\n' + ' -------------^\n', ): compile_with_func('a { content: returns_warning(); }') @@ -1183,7 +1230,7 @@ def test_error(self): ' on line 1 of stdin, in function `returns_error`\n' ' from line 1 of stdin\n' '>> a { content: returns_error(); }\n' - ' -------------^\n' + ' -------------^\n', ): compile_with_func('a { content: returns_error(); }') @@ -1205,7 +1252,7 @@ def test_returns_unknown_object(self): ' on line 1 of stdin, in function `returns_unknown`\n' ' from line 1 of stdin\n' '>> a { content: returns_unknown(); }\n' - ' -------------^\n' + ' -------------^\n', ): compile_with_func('a { content: returns_unknown(); }') @@ -1266,7 +1313,7 @@ def test_space_list(self): def test_bracketed_list(self): self.assertEqual( compile_with_func('a { content: returns_bracketed_list(); }'), - 'a{content:[hello ohai]}\n' + 'a{content:[hello ohai]}\n', ) def test_py_dict(self): @@ -1368,9 +1415,9 @@ def test_list_with_map_item(self): compile_with_func( 'a{content: ' 'map-get(nth(identity(((foo: bar), (baz: womp))), 1), foo)' - '}' + '}', ), - 'a{content:bar}\n' + 'a{content:bar}\n', ) def test_map_with_map_key(self): @@ -1392,7 +1439,7 @@ def test_stack_trace_formatting(): 'CompileError: Error: Invalid CSS after "a{☃": expected "{", was ""\n' ' on line 1 of stdin\n' '>> a{☃\n' - ' --^\n\n' + ' --^\n\n', ) @@ -1448,11 +1495,13 @@ def test_import_no_css(tmpdir): sass.compile(filename=main_scss.strpath) -@pytest.mark.parametrize('exts', [ - ('.css',), - ['.css'], - ['.foobar', '.css'], -]) +@pytest.mark.parametrize( + 'exts', [ + ('.css',), + ['.css'], + ['.foobar', '.css'], + ], +) def test_import_css(exts, tmpdir): tmpdir.join('other.css').write('body {color: green}') main_scss = tmpdir.join('main.scss') diff --git a/sassutils/builder.py b/sassutils/builder.py index 1e1da22e..919b2120 100644 --- a/sassutils/builder.py +++ b/sassutils/builder.py @@ -26,8 +26,10 @@ SUFFIX_PATTERN = re.compile('[.](' + '|'.join(map(re.escape, SUFFIXES)) + ')$') -def build_directory(sass_path, css_path, output_style='nested', - _root_sass=None, _root_css=None, strip_extension=False): +def build_directory( + sass_path, css_path, output_style='nested', + _root_sass=None, _root_css=None, strip_extension=False, +): """Compiles all Sass/SCSS files in ``path`` to CSS. :param sass_path: the path of the directory which contains source files @@ -61,9 +63,11 @@ def build_directory(sass_path, css_path, output_style='nested', if strip_extension: name, _ = os.path.splitext(name) css_fullname = os.path.join(css_path, name) + '.css' - css = compile(filename=sass_fullname, - output_style=output_style, - include_paths=[_root_sass]) + css = compile( + filename=sass_fullname, + output_style=output_style, + include_paths=[_root_sass], + ) with io.open( css_fullname, 'w', encoding='utf-8', newline='', ) as css_file: @@ -72,11 +76,13 @@ def build_directory(sass_path, css_path, output_style='nested', os.path.relpath(css_fullname, _root_css) elif os.path.isdir(sass_fullname): css_fullname = os.path.join(css_path, name) - subresult = build_directory(sass_fullname, css_fullname, - output_style=output_style, - _root_sass=_root_sass, - _root_css=_root_css, - strip_extension=strip_extension) + subresult = build_directory( + sass_fullname, css_fullname, + output_style=output_style, + _root_sass=_root_sass, + _root_css=_root_css, + strip_extension=strip_extension, + ) result.update(subresult) return result @@ -119,7 +125,7 @@ def normalize_manifests(cls, manifests): raise TypeError( 'manifest values must be a sassutils.builder.Manifest, ' 'a pair of (sass_path, css_path), or a string of ' - 'sass_path, not ' + repr(manifest) + 'sass_path, not ' + repr(manifest), ) manifests[package_name] = manifest return manifests @@ -205,10 +211,12 @@ def build(self, package_dir, output_style='nested'): css_files = build_directory( sass_path, css_path, output_style=output_style, - strip_extension=self.strip_extension + strip_extension=self.strip_extension, ).values() - return frozenset(os.path.join(self.css_path, filename) - for filename in css_files) + return frozenset( + os.path.join(self.css_path, filename) + for filename in css_files + ) def build_one(self, package_dir, filename, source_map=False): """Builds one Sass/SCSS file. @@ -230,7 +238,8 @@ def build_one(self, package_dir, filename, source_map=False): """ sass_filename, css_filename = self.resolve_filename( - package_dir, filename) + package_dir, filename, + ) root_path = os.path.join(package_dir, self.sass_path) css_path = os.path.join(package_dir, self.css_path, css_filename) if source_map: @@ -238,7 +247,7 @@ def build_one(self, package_dir, filename, source_map=False): css, source_map = compile( filename=sass_filename, include_paths=[root_path], - source_map_filename=source_map_path # FIXME + source_map_filename=source_map_path, # FIXME ) else: css = compile(filename=sass_filename, include_paths=[root_path]) diff --git a/sassutils/distutils.py b/sassutils/distutils.py index 21a2bd0a..80046f8c 100644 --- a/sassutils/distutils.py +++ b/sassutils/distutils.py @@ -96,7 +96,7 @@ def validate_manifests(dist, attr, value): attr + "must be a mapping object like: {'package.name': " "sassutils.distutils.Manifest('sass/path')}, or as shorten form: " "{'package.name': ('sass/path', 'css/path'}), not " + - repr(value) + repr(value), ) @@ -108,8 +108,8 @@ class build_sass(Command): ( 'output-style=', 's', 'Coding style of the compiled result. Choose one of ' + - ', '.join(OUTPUT_STYLES) - ) + ', '.join(OUTPUT_STYLES), + ), ] def initialize_options(self): @@ -134,7 +134,7 @@ def run(self): distutils.log.info("building '%s' sass", package_name) css_files = manifest.build( package_dir, - output_style=self.output_style + output_style=self.output_style, ) map(distutils.log.info, css_files) package_data.setdefault(package_name, []).extend(css_files) diff --git a/sassutils/wsgi.py b/sassutils/wsgi.py index a94a4c2f..408220be 100644 --- a/sassutils/wsgi.py +++ b/sassutils/wsgi.py @@ -89,8 +89,10 @@ class SassMiddleware(object): """ - def __init__(self, app, manifests, package_dir={}, - error_status='200 OK'): + def __init__( + self, app, manifests, package_dir={}, + error_status='200 OK', + ): if not callable(app): raise TypeError('app must be a WSGI-compliant callable object, ' 'not ' + repr(app)) @@ -125,9 +127,11 @@ def __call__(self, environ, start_response): css_filename = path[len(prefix):] sass_filename = css_filename[:-4] try: - result = manifest.build_one(package_dir, - sass_filename, - source_map=True) + result = manifest.build_one( + package_dir, + sass_filename, + source_map=True, + ) except (IOError, OSError): break except CompileError as e: @@ -135,7 +139,7 @@ def __call__(self, environ, start_response): logger.error(str(e)) start_response( self.error_status, - [('Content-Type', 'text/css; charset=utf-8')] + [('Content-Type', 'text/css; charset=utf-8')], ) return [ b'/*\n', str(e).encode('utf-8'), b'\n*/\n\n', @@ -144,7 +148,7 @@ def __call__(self, environ, start_response): b'; color: maroon; background-color: white', b'; white-space: pre-wrap; display: block', b'; font-family: "Courier New", monospace' - b'; user-select: text; }' + b'; user-select: text; }', ] def read_file(path): diff --git a/setup.py b/setup.py index 10b52ca5..79de909b 100644 --- a/setup.py +++ b/setup.py @@ -99,13 +99,14 @@ def _maybe_macos(flags): if sys.platform == 'win32': # This looks wrong, but is required for some reason :( version_define = r'/DLIBSASS_VERSION="\"{}\""'.format( - libsass_version) + libsass_version, + ) else: version_define = '-DLIBSASS_VERSION="{}"'.format(libsass_version) for directory in ( os.path.join('libsass', 'src'), - os.path.join('libsass', 'include') + os.path.join('libsass', 'include'), ): for pth, _, filenames in os.walk(directory): for filename in filenames: @@ -119,13 +120,15 @@ def _maybe_macos(flags): from distutils.msvc9compiler import get_build_version vscomntools_env = 'VS{}{}COMNTOOLS'.format( int(get_build_version()), - int(get_build_version() * 10) % 10 + int(get_build_version() * 10) % 10, ) try: os.environ[vscomntools_env] = os.environ['VS140COMNTOOLS'] except KeyError: - distutils.log.warn('You probably need Visual Studio 2015 (14.0) ' - 'or higher') + distutils.log.warn( + 'You probably need Visual Studio 2015 (14.0) ' + 'or higher', + ) from distutils import msvccompiler, msvc9compiler if msvccompiler.get_build_version() < 14.0: msvccompiler.get_build_version = lambda: 14.0 @@ -154,13 +157,13 @@ def _maybe_macos(flags): f.write( '#ifdef __cplusplus\n' 'extern "C" {\n' - '#endif\n' + '#endif\n', ) f.write(cencode_body) f.write( '#ifdef __cplusplus\n' '}\n' - '#endif\n' + '#endif\n', ) @atexit.register @@ -186,7 +189,7 @@ def restore_cencode(): depends=headers, extra_compile_args=extra_compile_args, extra_link_args=link_flags, - libraries=libraries + libraries=libraries, ) @@ -224,11 +227,15 @@ def finalize_options(self): def run(self): path = tempfile.mkdtemp() - build = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'build', 'sphinx', 'html') + build = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'build', 'sphinx', 'html', + ) os.chdir(path) - os.system('git clone -b gh-pages --depth 5 ' - 'git@github.com:sass/libsass-python.git .') + os.system( + 'git clone -b gh-pages --depth 5 ' + 'git@github.com:sass/libsass-python.git .', + ) os.system('git rm -r .') os.system('touch .nojekyll') os.system('cp -r ' + build + '/* .') @@ -250,8 +257,8 @@ def run(self): package_data={ '': [ 'README.rst', - 'test/*.sass' - ] + 'test/*.sass', + ], }, scripts=['sassc.py'], license='MIT License', @@ -261,16 +268,16 @@ def run(self): download_url='https://github.com/sass/libsass-python/releases', entry_points={ 'distutils.commands': [ - 'build_sass = sassutils.distutils:build_sass' + 'build_sass = sassutils.distutils:build_sass', ], 'distutils.setup_keywords': [ - 'sass_manifests = sassutils.distutils:validate_manifests' + 'sass_manifests = sassutils.distutils:validate_manifests', ], 'console_scripts': [ ['pysassc = sassc:main'], # TODO: deprecate `sassc` and remove (#134) ['sassc = sassc:main'], - ] + ], }, install_requires=['six'], extras_require={'upload_appveyor_builds': ['twine == 1.11.0']}, @@ -293,7 +300,7 @@ def run(self): 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Code Generators', - 'Topic :: Software Development :: Compilers' + 'Topic :: Software Development :: Compilers', ], - cmdclass={'upload_doc': upload_doc} + cmdclass={'upload_doc': upload_doc}, ) diff --git a/testpkg/setup.py b/testpkg/setup.py index 04032f68..b9d42275 100644 --- a/testpkg/setup.py +++ b/testpkg/setup.py @@ -5,7 +5,7 @@ name='testpkg', packages=['testpkg'], sass_manifests={ - 'testpkg': ('static/scss', 'static/css') + 'testpkg': ('static/scss', 'static/css'), }, - setup_requires=['libsass'] + setup_requires=['libsass'], ) diff --git a/upload_appveyor_builds.py b/upload_appveyor_builds.py index 0832850e..00c5c1e8 100755 --- a/upload_appveyor_builds.py +++ b/upload_appveyor_builds.py @@ -14,12 +14,18 @@ APPVEYOR_API_BASE_URL = 'https://ci.appveyor.com/api/' -APPVEYOR_API_PROJECT_URL = urljoin(APPVEYOR_API_BASE_URL, - 'projects/asottile/libsass-python/') -APPVEYOR_API_BUILDS_URL = urljoin(APPVEYOR_API_PROJECT_URL, - 'history?recordsNumber=50&branch=master') -APPVEYOR_API_JOBS_URL = urljoin(APPVEYOR_API_PROJECT_URL, - 'build/') +APPVEYOR_API_PROJECT_URL = urljoin( + APPVEYOR_API_BASE_URL, + 'projects/asottile/libsass-python/', +) +APPVEYOR_API_BUILDS_URL = urljoin( + APPVEYOR_API_PROJECT_URL, + 'history?recordsNumber=50&branch=master', +) +APPVEYOR_API_JOBS_URL = urljoin( + APPVEYOR_API_PROJECT_URL, + 'build/', +) APPVEYOR_API_JOB_URL = urljoin(APPVEYOR_API_BASE_URL, 'buildjobs/') @@ -66,8 +72,10 @@ def ci_jobs(build): def ci_artifacts(job): - url = urljoin(urljoin(APPVEYOR_API_JOB_URL, job['jobId'] + '/'), - 'artifacts/') + url = urljoin( + urljoin(APPVEYOR_API_JOB_URL, job['jobId'] + '/'), + 'artifacts/', + ) response = urlopen(url) files = json.load(response) response.close() @@ -97,14 +105,20 @@ def download_artifact(artifact, target_dir, overwrite=False): def main(): parser = argparse.ArgumentParser() - parser.add_argument('--overwrite', action='store_true', default=False, - help='Overwrite files if already exist') - parser.add_argument('--dist-dir', default='./dist/', - help='The temporary directory to download artifacts') + parser.add_argument( + '--overwrite', action='store_true', default=False, + help='Overwrite files if already exist', + ) + parser.add_argument( + '--dist-dir', default='./dist/', + help='The temporary directory to download artifacts', + ) parser.add_argument( 'tag', - help=('Git tag of the version to upload. If it has a leading slash, ' - 'it means AppVeyor build number rather than Git tag.') + help=( + 'Git tag of the version to upload. If it has a leading slash, ' + 'it means AppVeyor build number rather than Git tag.' + ), ) args = parser.parse_args() if args.tag.startswith('/'):