From d333e3c419f48f4b2d96528cb4c83db1f02adaec Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 18 Nov 2021 19:02:04 -0500 Subject: [PATCH] drop python 2.x --- .pre-commit-config.yaml | 2 + README.rst | 2 +- azure-pipelines.yml | 13 +---- bin/build-manylinux-wheels | 6 +-- docs/conf.py | 17 +++--- docs/index.rst | 2 +- pysassc.py | 8 ++- sass.py | 87 +++++++++++++------------------ sasstests.py | 103 ++++++++++++++++++------------------- sassutils/_compat.py | 7 --- sassutils/builder.py | 28 +++++----- sassutils/distutils.py | 1 - sassutils/wsgi.py | 9 ++-- setup.py | 13 ++--- tox.ini | 2 +- 15 files changed, 129 insertions(+), 171 deletions(-) delete mode 100644 sassutils/_compat.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba717119..5c782a74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,9 @@ repos: rev: v2.29.0 hooks: - id: pyupgrade + args: [--py36-plus] - repo: https://github.com/asottile/add-trailing-comma rev: v2.2.0 hooks: - id: add-trailing-comma + args: [--py36-plus] diff --git a/README.rst b/README.rst index e11625d8..9345f4bd 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ distribution/deployment. That means you can add just ``libsass`` into your ``setup.py``'s ``install_requires`` list or ``requirements.txt`` file. No need for Ruby nor Node.js. -It currently supports CPython 2.7, 3.6--3.8, and PyPy 2.3+! +It currently supports CPython 3.6+, and PyPy 3! .. _Sass: https://sass-lang.com/ .. _LibSass: https://github.com/sass/libsass diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ac189740..60895331 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,18 +18,9 @@ resources: jobs: - template: job--python-tox.yml@asottile parameters: - toxenvs: [py27, py36] + toxenvs: [py36] os: macos wheel_tags: true -- template: job--python-tox.yml@asottile - parameters: - toxenvs: [py27] - os: windows - architectures: [x64, x86] - name_postfix: _py27 - wheel_tags: true - pre_test: - - script: rm -rf libsass/test - template: job--python-tox.yml@asottile parameters: toxenvs: [py36] @@ -38,5 +29,5 @@ jobs: wheel_tags: true - template: job--python-tox.yml@asottile parameters: - toxenvs: [pypy, pypy3, py27, py36, py37, py38, py39] + toxenvs: [pypy3, py36, py37, py38, py39] os: linux diff --git a/bin/build-manylinux-wheels b/bin/build-manylinux-wheels index c0f7fc63..70958b43 100755 --- a/bin/build-manylinux-wheels +++ b/bin/build-manylinux-wheels @@ -23,13 +23,13 @@ def main(): os.makedirs('dist', exist_ok=True) for python in ('cp27-cp27mu', 'cp36-cp36m'): with tempfile.TemporaryDirectory() as work: - pip = '/opt/python/{}/bin/pip'.format(python) + pip = f'/opt/python/{python}/bin/pip' check_call( 'docker', 'run', '-ti', # Use this so the files are not owned by root - '--user', '{}:{}'.format(os.getuid(), os.getgid()), + '--user', f'{os.getuid()}:{os.getgid()}', # We'll do building in /work and copy results to /dist - '-v', '{}:/work:rw'.format(work), + '-v', f'{work}:/work:rw', '-v', '{}:/dist:rw'.format(os.path.abspath('dist')), 'quay.io/pypa/manylinux1_x86_64:latest', 'bash', '-exc', diff --git a/docs/conf.py b/docs/conf.py index 921d65c8..f0f0d617 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # libsass documentation build configuration file, created by # sphinx-quickstart on Sun Aug 19 22:45:57 2012. @@ -48,8 +47,8 @@ master_doc = 'index' # General information about the project. -project = u'libsass' -copyright = u'2012, Hong Minhee' +project = 'libsass' +copyright = '2012, Hong Minhee' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -194,8 +193,8 @@ # (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', 'libsass Documentation', + 'Hong Minhee', 'manual', ), ] @@ -226,8 +225,8 @@ # (source start file, name, description, authors, manual section). man_pages = [ ( - 'index', 'libsass', u'libsass Documentation', - [u'Hong Minhee'], 1, + 'index', 'libsass', 'libsass Documentation', + ['Hong Minhee'], 1, ), ] @@ -242,8 +241,8 @@ # dir menu entry, description, category) texinfo_documents = [ ( - 'index', 'libsass', u'libsass Documentation', - u'Hong Minhee', 'libsass', 'One line description of project.', + 'index', 'libsass', 'libsass Documentation', + 'Hong Minhee', 'libsass', 'One line description of project.', 'Miscellaneous', ), ] diff --git a/docs/index.rst b/docs/index.rst index 2613f792..e571b487 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,7 +8,7 @@ distribution/deployment. That means you can add just ``libsass`` into your :file:`setup.py`'s ``install_requires`` list or :file:`requirements.txt` file. -It currently supports CPython 2.6, 2.7, 3.5--3.7, and PyPy 2.3+! +It currently supports CPython 3.6+ and PyPy 3! .. _Sass: https://sass-lang.com/ .. _LibSass: https://github.com/sass/libsass diff --git a/pysassc.py b/pysassc.py index b3ef1b0f..aa5a91d6 100755 --- a/pysassc.py +++ b/pysassc.py @@ -88,10 +88,8 @@ .. _SassC: https://github.com/sass/sassc """ -from __future__ import print_function import functools -import io import optparse import sys import warnings @@ -219,7 +217,7 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): include_paths=options.include_paths, precision=options.precision, ) - except (IOError, OSError) as e: + except OSError as e: error(e) return 3 except sass.CompileError as e: @@ -229,10 +227,10 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): if len(args) < 2: print(css, file=stdout) else: - with io.open(args[1], 'w', encoding='utf-8', newline='') as f: + with open(args[1], 'w', encoding='utf-8', newline='') as f: f.write(css) if source_map_filename: - with io.open( + with open( source_map_filename, 'w', encoding='utf-8', newline='', ) as f: f.write(source_map) diff --git a/sass.py b/sass.py index 8bc0dd88..86992e68 100644 --- a/sass.py +++ b/sass.py @@ -10,21 +10,15 @@ 'a b {\n color: blue; }\n' """ -from __future__ import absolute_import -import collections +import collections.abc import inspect -import io -import os import os.path import re import sys import warnings -from six import string_types, text_type, PY2 - import _sass -from sassutils._compat import collections_abc __all__ = ( 'MODES', 'OUTPUT_STYLES', 'SOURCE_COMMENTS', 'CompileError', 'SassColor', @@ -52,11 +46,10 @@ def to_native_s(s): - if isinstance(s, bytes) and not PY2: # pragma: no cover (py3) - s = s.decode('UTF-8') - elif isinstance(s, text_type) and PY2: # pragma: no cover (py2) - s = s.encode('UTF-8') - return s + if isinstance(s, bytes): + return s.decode('UTF-8') + else: + return s class CompileError(ValueError): @@ -64,7 +57,7 @@ class CompileError(ValueError): It is a subtype of :exc:`exceptions.ValueError`. """ def __init__(self, msg): - super(CompileError, self).__init__(to_native_s(msg)) + super().__init__(to_native_s(msg)) def mkdirp(path): @@ -76,7 +69,7 @@ def mkdirp(path): raise -class SassFunction(object): +class SassFunction: """Custom function for Sass. It can be instantiated using :meth:`from_lambda()` and :meth:`from_named_function()` as well. @@ -107,16 +100,10 @@ def from_lambda(cls, name, lambda_): :rtype: :class:`SassFunction` """ - if PY2: # pragma: no cover - a = inspect.getargspec(lambda_) - varargs, varkw, defaults, kwonlyargs = ( - 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 = inspect.getfullargspec(lambda_) + varargs, varkw, defaults, kwonlyargs = ( + a.varargs, a.varkw, a.defaults, a.kwonlyargs, + ) if varargs or varkw or defaults or kwonlyargs: raise TypeError( @@ -142,9 +129,9 @@ def from_named_function(cls, function): return cls.from_lambda(function.__name__, function) def __init__(self, name, arguments, callable_): - if not isinstance(name, string_types): + if not isinstance(name, str): raise TypeError('name must be a string, not ' + repr(name)) - elif not isinstance(arguments, collections_abc.Sequence): + elif not isinstance(arguments, collections.abc.Sequence): raise TypeError( 'arguments must be a sequence, not ' + repr(arguments), @@ -263,7 +250,7 @@ def compile_dirname( if s: v = v.decode('UTF-8') mkdirp(os.path.dirname(output_filename)) - with io.open( + with open( output_filename, 'w', encoding='UTF-8', newline='', ) as output_file: output_file.write(v) @@ -277,7 +264,7 @@ def _check_no_remaining_kwargs(func, kwargs): raise TypeError( '{}() got unexpected keyword argument(s) {}'.format( func.__name__, - ', '.join("'{}'".format(arg) for arg in sorted(kwargs)), + ', '.join(f"'{arg}'" for arg in sorted(kwargs)), ), ) @@ -563,7 +550,7 @@ def my_importer(path, prev): ) precision = kwargs.pop('precision', 5) output_style = kwargs.pop('output_style', 'nested') - if not isinstance(output_style, string_types): + if not isinstance(output_style, str): raise TypeError( 'output_style must be a string, not ' + repr(output_style), @@ -612,9 +599,9 @@ def my_importer(path, prev): 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): + if ret is not None and not isinstance(ret, str): + raise TypeError(f'{key} must be a string, not {ret!r}') + elif isinstance(ret, str): ret = ret.encode(fs_encoding) if ret and 'filename' not in modes: raise CompileError( @@ -631,25 +618,25 @@ def _get_file_arg(key): omit_source_map_url = kwargs.pop('omit_source_map_url', False) source_map_root = kwargs.pop('source_map_root', None) - if isinstance(source_map_root, text_type): + if isinstance(source_map_root, str): source_map_root = source_map_root.encode('utf-8') # #208: cwd is always included in include paths include_paths = (os.getcwd(),) include_paths += tuple(kwargs.pop('include_paths', ()) or ()) include_paths = os.pathsep.join(include_paths) - if isinstance(include_paths, text_type): + if isinstance(include_paths, str): include_paths = include_paths.encode(fs_encoding) custom_functions = kwargs.pop('custom_functions', ()) - if isinstance(custom_functions, collections_abc.Mapping): + if isinstance(custom_functions, collections.abc.Mapping): custom_functions = [ SassFunction.from_lambda(name, lambda_) for name, lambda_ in custom_functions.items() ] elif isinstance( custom_functions, - (collections_abc.Set, collections_abc.Sequence), + (collections.abc.Set, collections.abc.Sequence), ): custom_functions = [ func if isinstance(func, SassFunction) @@ -676,7 +663,7 @@ def _get_file_arg(key): if 'string' in modes: string = kwargs.pop('string') - if isinstance(string, text_type): + if isinstance(string, str): string = string.encode('utf-8') indented = kwargs.pop('indented', False) if not isinstance(indented, bool): @@ -695,11 +682,11 @@ def _get_file_arg(key): return v.decode('utf-8') elif 'filename' in modes: filename = kwargs.pop('filename') - if not isinstance(filename, string_types): + if not isinstance(filename, str): raise TypeError('filename must be a string, not ' + repr(filename)) elif not os.path.isfile(filename): - raise IOError('{!r} seems not a file'.format(filename)) - elif isinstance(filename, text_type): + raise OSError(f'{filename!r} seems not a file') + elif isinstance(filename, str): filename = filename.encode(fs_encoding) _check_no_remaining_kwargs(compile, kwargs) s, v, source_map = _sass.compile_filename( @@ -780,9 +767,9 @@ class SassNumber(collections.namedtuple('SassNumber', ('value', 'unit'))): def __new__(cls, value, unit): value = float(value) - if not isinstance(unit, text_type): + if not isinstance(unit, str): unit = unit.decode('UTF-8') - return super(SassNumber, cls).__new__(cls, value, unit) + return super().__new__(cls, value, unit) class SassColor(collections.namedtuple('SassColor', ('r', 'g', 'b', 'a'))): @@ -792,7 +779,7 @@ def __new__(cls, r, g, b, a): g = float(g) b = float(b) a = float(a) - return super(SassColor, cls).__new__(cls, r, g, b, a) + return super().__new__(cls, r, g, b, a) SASS_SEPARATOR_COMMA = collections.namedtuple('SASS_SEPARATOR_COMMA', ())() @@ -810,26 +797,26 @@ def __new__(cls, items, separator, bracketed=False): items = tuple(items) assert separator in SEPARATORS, separator assert isinstance(bracketed, bool), bracketed - return super(SassList, cls).__new__(cls, items, separator, bracketed) + return super().__new__(cls, items, separator, bracketed) class SassError(collections.namedtuple('SassError', ('msg',))): def __new__(cls, msg): - if not isinstance(msg, text_type): + if not isinstance(msg, str): msg = msg.decode('UTF-8') - return super(SassError, cls).__new__(cls, msg) + return super().__new__(cls, msg) class SassWarning(collections.namedtuple('SassWarning', ('msg',))): def __new__(cls, msg): - if not isinstance(msg, text_type): + if not isinstance(msg, str): msg = msg.decode('UTF-8') - return super(SassWarning, cls).__new__(cls, msg) + return super().__new__(cls, msg) -class SassMap(collections_abc.Mapping): +class SassMap(collections.abc.Mapping): """Because sass maps can have mapping types as keys, we need an immutable hashable mapping type. @@ -858,7 +845,7 @@ def __len__(self): # Our interface def __repr__(self): - return '{}({})'.format(type(self).__name__, frozenset(self.items())) + return f'{type(self).__name__}({frozenset(self.items())})' def __hash__(self): return self._hash diff --git a/sasstests.py b/sasstests.py index 6aaf5539..c901e561 100644 --- a/sasstests.py +++ b/sasstests.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- - import base64 +import collections.abc import contextlib import functools import glob -import json import io +import json import os import os.path import re @@ -17,14 +16,12 @@ import unittest import pytest -from six import StringIO, b, string_types, text_type from werkzeug.test import Client from werkzeug.wrappers import Response import pysassc import sass import sassc -from sassutils._compat import collections_abc from sassutils.builder import Manifest, build_directory from sassutils.wsgi import SassMiddleware @@ -71,7 +68,7 @@ def set_coverage_instrumentation(): ), } -with io.open('test/a.scss', newline='') as f: +with open('test/a.scss', newline='') as f: A_EXPECTED_MAP_CONTENTS = dict(A_EXPECTED_MAP, sourcesContent=[f.read()]) B_EXPECTED_CSS = '''\ @@ -95,7 +92,7 @@ def set_coverage_instrumentation(): color: green; } ''' -D_EXPECTED_CSS = u'''\ +D_EXPECTED_CSS = '''\ @charset "UTF-8"; body { background-color: green; } @@ -103,7 +100,7 @@ def set_coverage_instrumentation(): font: '나눔고딕', sans-serif; } ''' -D_EXPECTED_CSS_WITH_MAP = u'''\ +D_EXPECTED_CSS_WITH_MAP = '''\ @charset "UTF-8"; body { background-color: green; } @@ -149,7 +146,7 @@ def set_coverage_instrumentation(): def _map_in_output_dir(s): def cb(match): filename = os.path.basename(match.group(1)) - return '/*# sourceMappingURL={} */'.format(filename) + return f'/*# sourceMappingURL={filename} */' return re_sourcemap_url.sub(cb, s) @@ -163,9 +160,9 @@ def no_warnings(recwarn): class BaseTestCase(unittest.TestCase): def assert_source_map_equal(self, expected, actual): - if isinstance(expected, string_types): + if isinstance(expected, str): expected = json.loads(expected) - if isinstance(actual, string_types): + if isinstance(actual, str): actual = json.loads(actual) assert expected == actual @@ -175,7 +172,7 @@ def assert_source_map_file(self, expected, filename): tree = json.load(f) except ValueError as e: # pragma: no cover f.seek(0) - msg = '{!s}\n\n{}:\n\n{}'.format(e, filename, f.read()) + msg = f'{e!s}\n\n{filename}:\n\n{f.read()}' raise ValueError(msg) self.assert_source_map_equal(expected, tree) @@ -196,7 +193,7 @@ def test_version(self): assert re.match(r'^\d+\.\d+\.\d+$', sass.__version__) def test_output_styles(self): - assert isinstance(sass.OUTPUT_STYLES, collections_abc.Mapping) + assert isinstance(sass.OUTPUT_STYLES, collections.abc.Mapping) assert 'nested' in sass.OUTPUT_STYLES def test_and_join(self): @@ -294,9 +291,9 @@ def test_compile_string(self): a b { color: blue; } ''' - actual = sass.compile(string=u'a { color: blue; } /* 유니코드 */') + actual = sass.compile(string='a { color: blue; } /* 유니코드 */') self.assertEqual( - u'''@charset "UTF-8"; + '''@charset "UTF-8"; a { color: blue; } @@ -330,11 +327,11 @@ def test_compile_file_sass_style(self): def test_importer_one_arg(self): """Demonstrates one-arg importers + chaining.""" def importer_returning_one_argument(path): - assert type(path) is text_type + assert type(path) is str return ( # Trigger the import of an actual file ('test/b.scss',), - (path, '.{0}-one-arg {{ color: blue; }}'.format(path)), + (path, f'.{path}-one-arg {{ color: blue; }}'), ) ret = sass.compile( @@ -448,7 +445,7 @@ def importer_with_srcmap(path): def test_importers_raises_exception(self): def importer(path): - raise ValueError('Bad path: {}'.format(path)) + raise ValueError(f'Bad path: {path}') with assert_raises_compile_error( RegexMatcher( @@ -640,31 +637,31 @@ def test_builder_build_directory(self): result_files = build_directory(self.sass_path, css_path) assert len(result_files) == 8 assert 'a.scss.css' == result_files['a.scss'] - with io.open( + with open( os.path.join(css_path, 'a.scss.css'), encoding='UTF-8', ) as f: css = f.read() assert A_EXPECTED_CSS == css assert 'b.scss.css' == result_files['b.scss'] - with io.open( + with open( os.path.join(css_path, 'b.scss.css'), encoding='UTF-8', ) as f: css = f.read() assert B_EXPECTED_CSS == css assert 'c.scss.css' == result_files['c.scss'] - with io.open( + with open( os.path.join(css_path, 'c.scss.css'), encoding='UTF-8', ) as f: css = f.read() assert C_EXPECTED_CSS == css assert 'd.scss.css' == result_files['d.scss'] - with io.open( + with open( os.path.join(css_path, 'd.scss.css'), encoding='UTF-8', ) as f: css = f.read() assert D_EXPECTED_CSS == css assert 'e.scss.css' == result_files['e.scss'] - with io.open( + with open( os.path.join(css_path, 'e.scss.css'), encoding='UTF-8', ) as f: css = f.read() @@ -673,7 +670,7 @@ def test_builder_build_directory(self): os.path.join('subdir', 'recur.scss.css'), result_files[os.path.join('subdir', 'recur.scss')], ) - with io.open( + with open( os.path.join(css_path, 'g.scss.css'), encoding='UTF-8', ) as f: css = f.read() @@ -683,12 +680,12 @@ def test_builder_build_directory(self): result_files[os.path.join('subdir', 'recur.scss')], ) assert 'h.sass.css' == result_files['h.sass'] - with io.open( + with open( os.path.join(css_path, 'h.sass.css'), encoding='UTF-8', ) as f: css = f.read() assert H_EXPECTED_CSS == css - with io.open( + with open( os.path.join(css_path, 'subdir', 'recur.scss.css'), encoding='UTF-8', ) as f: @@ -703,7 +700,7 @@ def test_output_style(self): ) assert len(result_files) == 8 assert 'a.scss.css' == result_files['a.scss'] - with io.open( + with open( os.path.join(css_path, 'a.scss.css'), encoding='UTF-8', ) as f: css = f.read() @@ -755,7 +752,7 @@ def test_build_one(self): with open(os.path.join(d, 'css', 'a.scss.css')) as f: assert A_EXPECTED_CSS == f.read() m.build_one(d, 'b.scss', source_map=True) - with io.open( + with open( os.path.join(d, 'css', 'b.scss.css'), encoding='UTF-8', ) as f: assert f.read() == _map_in_output_dir(B_EXPECTED_CSS_WITH_MAP) @@ -773,7 +770,7 @@ def test_build_one(self): os.path.join(d, 'css', 'b.scss.css.map'), ) m.build_one(d, 'd.scss', source_map=True) - with io.open( + with open( os.path.join(d, 'css', 'd.scss.css'), encoding='UTF-8', ) as f: assert f.read() == _map_in_output_dir(D_EXPECTED_CSS_WITH_MAP) @@ -838,7 +835,7 @@ def test_wsgi_sass_middleware(self): r = client.get('/static/a.scss.css') assert r.status_code == 200 self.assertEqual( - b(_map_in_output_dir(A_EXPECTED_CSS_WITH_MAP)), + _map_in_output_dir(A_EXPECTED_CSS_WITH_MAP).encode(), r.data, ) assert r.mimetype == 'text/css' @@ -903,7 +900,7 @@ def css_path(self, *args): return os.path.join( os.path.dirname(__file__), 'testpkg', 'testpkg', 'static', 'css', - *args + *args, ) def list_built_css(self): @@ -942,8 +939,8 @@ def test_output_style(self): class SasscTestCase(BaseTestCase): def setUp(self): - self.out = StringIO() - self.err = StringIO() + self.out = io.StringIO() + self.err = io.StringIO() def test_no_args(self): exit_code = pysassc.main(['pysassc'], self.out, self.err) @@ -995,7 +992,7 @@ def test_pysassc_output(self): assert exit_code == 0 assert self.err.getvalue() == '' assert self.out.getvalue() == '' - with io.open(tmp, encoding='UTF-8', newline='') as f: + with open(tmp, encoding='UTF-8', newline='') as f: assert A_EXPECTED_CSS.strip() == f.read().strip() finally: os.remove(tmp) @@ -1011,7 +1008,7 @@ def test_pysassc_output_unicode(self): assert exit_code == 0 assert self.err.getvalue() == '' assert self.out.getvalue() == '' - with io.open(tmp, encoding='UTF-8') as f: + with open(tmp, encoding='UTF-8') as f: assert D_EXPECTED_CSS.strip() == f.read().strip() finally: os.remove(tmp) @@ -1093,10 +1090,10 @@ def test_compile_directories_unicode(self): input_dir = os.path.join(tmpdir, 'input') output_dir = os.path.join(tmpdir, 'output') os.makedirs(input_dir) - with io.open( + with open( os.path.join(input_dir, 'test.scss'), 'w', encoding='UTF-8', ) as f: - f.write(u'a { content: "☃"; }') + f.write('a { content: "☃"; }') # Raised a UnicodeEncodeError in py2 before #82 (issue #72) # Also raised a UnicodeEncodeError in py3 if the default encoding # couldn't represent it (such as cp1252 on windows) @@ -1164,14 +1161,14 @@ def test_sass_func_type_errors(func): class SassTypesTest(unittest.TestCase): def test_number_no_conversion(self): - num = sass.SassNumber(123., u'px') + num = sass.SassNumber(123., 'px') assert type(num.value) is float, type(num.value) - assert type(num.unit) is text_type, type(num.unit) + assert type(num.unit) is str, type(num.unit) def test_number_conversion(self): num = sass.SassNumber(123, b'px') assert type(num.value) is float, type(num.value) - assert type(num.unit) is text_type, type(num.unit) + assert type(num.unit) is str, type(num.unit) def test_color_no_conversion(self): color = sass.SassColor(1., 2., 3., .5) @@ -1198,20 +1195,20 @@ def test_sass_list_conversion(self): assert lst.separator is sass.SASS_SEPARATOR_SPACE, lst.separator def test_sass_warning_no_conversion(self): - warn = sass.SassWarning(u'error msg') - assert type(warn.msg) is text_type, type(warn.msg) + warn = sass.SassWarning('error msg') + assert type(warn.msg) is str, type(warn.msg) def test_sass_warning_no_conversion_bytes_message(self): warn = sass.SassWarning(b'error msg') - assert type(warn.msg) is text_type, type(warn.msg) + assert type(warn.msg) is str, type(warn.msg) def test_sass_error_no_conversion(self): - err = sass.SassError(u'error msg') - assert type(err.msg) is text_type, type(err.msg) + err = sass.SassError('error msg') + assert type(err.msg) is str, type(err.msg) def test_sass_error_conversion(self): err = sass.SassError(b'error msg') - assert type(err.msg) is text_type, type(err.msg) + assert type(err.msg) is str, type(err.msg) def raises(): @@ -1244,11 +1241,11 @@ def returns_none(): def returns_unicode(): - return u'☃' + return '☃' def returns_bytes(): - return u'☃'.encode('UTF-8') + return '☃'.encode() def returns_number(): @@ -1380,7 +1377,7 @@ def assert_raises_compile_error(expected): assert msg == expected, (msg, expected) -class RegexMatcher(object): +class RegexMatcher: def __init__(self, reg, flags=None): self.reg = re.compile(reg, re.MULTILINE | re.DOTALL) @@ -1472,13 +1469,13 @@ def test_false(self): def test_unicode(self): self.assertEqual( compile_with_func('a { content: returns_unicode(); }'), - u'\ufeffa{content:☃}\n', + '\ufeffa{content:☃}\n', ) def test_bytes(self): self.assertEqual( compile_with_func('a { content: returns_bytes(); }'), - u'\ufeffa{content:☃}\n', + '\ufeffa{content:☃}\n', ) def test_number(self): @@ -1550,7 +1547,7 @@ def test_identity_false(self): def test_identity_strings(self): self.assertEqual( compile_with_func('a { content: identity(returns_unicode()); }'), - u'\ufeffa{content:☃}\n', + '\ufeffa{content:☃}\n', ) def test_identity_number(self): @@ -1626,7 +1623,7 @@ def test_map_with_map_key(self): def test_stack_trace_formatting(): try: - sass.compile(string=u'a{☃') + sass.compile(string='a{☃') raise AssertionError('expected to raise CompileError') except sass.CompileError: tb = traceback.format_exc() diff --git a/sassutils/_compat.py b/sassutils/_compat.py deleted file mode 100644 index 8f794ac8..00000000 --- a/sassutils/_compat.py +++ /dev/null @@ -1,7 +0,0 @@ -from six import PY2 - - -if PY2: # pragma: no cover (PY2) - import collections as collections_abc # noqa: F401 -else: # pragma: no cover (PY3) - import collections.abc as collections_abc # noqa: F401 diff --git a/sassutils/builder.py b/sassutils/builder.py index b07dfad7..747b5b40 100644 --- a/sassutils/builder.py +++ b/sassutils/builder.py @@ -3,16 +3,12 @@ """ -import io -import os +import collections.abc import os.path import re import warnings -from six import string_types - from sass import compile -from sassutils._compat import collections_abc __all__ = 'SUFFIXES', 'SUFFIX_PATTERN', 'Manifest', 'build_directory' @@ -69,7 +65,7 @@ def build_directory( output_style=output_style, include_paths=[_root_sass], ) - with io.open( + with open( css_fullname, 'w', encoding='utf-8', newline='', ) as css_file: css_file.write(css) @@ -88,7 +84,7 @@ def build_directory( return result -class Manifest(object): +class Manifest: """Building manifest of Sass/SCSS. :param sass_path: the path of the directory that contains Sass/SCSS @@ -105,7 +101,7 @@ class Manifest(object): def normalize_manifests(cls, manifests): if manifests is None: manifests = {} - elif isinstance(manifests, collections_abc.Mapping): + elif isinstance(manifests, collections.abc.Mapping): manifests = dict(manifests) else: raise TypeError( @@ -113,7 +109,7 @@ def normalize_manifests(cls, manifests): repr(manifests), ) for package_name, manifest in manifests.items(): - if not isinstance(package_name, string_types): + if not isinstance(package_name, str): raise TypeError( 'manifest keys must be a string of package ' 'name, not ' + repr(package_name), @@ -122,9 +118,9 @@ def normalize_manifests(cls, manifests): continue elif isinstance(manifest, tuple): manifest = Manifest(*manifest) - elif isinstance(manifest, collections_abc.Mapping): + elif isinstance(manifest, collections.abc.Mapping): manifest = Manifest(**manifest) - elif isinstance(manifest, string_types): + elif isinstance(manifest, str): manifest = Manifest(manifest) else: raise TypeError( @@ -142,21 +138,21 @@ def __init__( wsgi_path=None, strip_extension=None, ): - if not isinstance(sass_path, string_types): + if not isinstance(sass_path, str): raise TypeError( 'sass_path must be a string, not ' + repr(sass_path), ) if css_path is None: css_path = sass_path - elif not isinstance(css_path, string_types): + elif not isinstance(css_path, str): raise TypeError( 'css_path must be a string, not ' + repr(css_path), ) if wsgi_path is None: wsgi_path = css_path - elif not isinstance(wsgi_path, string_types): + elif not isinstance(wsgi_path, str): raise TypeError( 'wsgi_path must be a string, not ' + repr(wsgi_path), @@ -292,11 +288,11 @@ def build_one(self, package_dir, filename, source_map=False): css_folder = os.path.dirname(css_path) if not os.path.exists(css_folder): os.makedirs(css_folder) - with io.open(css_path, 'w', encoding='utf-8', newline='') as f: + with open(css_path, 'w', encoding='utf-8', newline='') as f: f.write(css) if source_map: # Source maps are JSON, and JSON has to be UTF-8 encoded - with io.open( + with open( source_map_path, 'w', encoding='utf-8', newline='', ) as f: f.write(source_map) diff --git a/sassutils/distutils.py b/sassutils/distutils.py index 52b19c67..ab178038 100644 --- a/sassutils/distutils.py +++ b/sassutils/distutils.py @@ -67,7 +67,6 @@ Added ``--output-style``/``-s`` option to :class:`build_sass` command. """ -from __future__ import absolute_import import distutils.errors import distutils.log diff --git a/sassutils/wsgi.py b/sassutils/wsgi.py index 2d8a5543..56c2769c 100644 --- a/sassutils/wsgi.py +++ b/sassutils/wsgi.py @@ -2,8 +2,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ -from __future__ import absolute_import +import collections.abc import logging import os import os.path @@ -11,13 +11,12 @@ from pkg_resources import resource_filename from sass import CompileError -from sassutils._compat import collections_abc from .builder import Manifest __all__ = 'SassMiddleware', -class SassMiddleware(object): +class SassMiddleware: r"""WSGI middleware for development purpose. Every time a CSS file has requested it finds a matched Sass/SCSS source file and then compiled it into CSS. @@ -100,7 +99,7 @@ def __init__( ) self.app = app self.manifests = Manifest.normalize_manifests(manifests) - if not isinstance(package_dir, collections_abc.Mapping): + if not isinstance(package_dir, collections.abc.Mapping): raise TypeError( 'package_dir must be a mapping object, not ' + repr(package_dir), @@ -138,7 +137,7 @@ def __call__(self, environ, start_response): sass_filename, source_map=True, ) - except (IOError, OSError): + except OSError: break except CompileError as e: logger = logging.getLogger(__name__ + '.SassMiddleware') diff --git a/setup.py b/setup.py index b1eb95a1..2bbf408c 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import ast import atexit import distutils.cmd @@ -82,9 +80,9 @@ def customize_compiler(compiler): libsass_version = libsass_version_file.read().decode('UTF-8').strip() if sys.platform == 'win32': # This looks wrong, but is required for some reason :( - define = r'/DLIBSASS_VERSION="\"{}\""'.format(libsass_version) + define = fr'/DLIBSASS_VERSION="\"{libsass_version}\""' else: - define = '-DLIBSASS_VERSION="{}"'.format(libsass_version) + define = f'-DLIBSASS_VERSION="{libsass_version}"' for directory in ( os.path.join('libsass', 'src'), @@ -184,7 +182,7 @@ def readme(): try: with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f: return f.read() - except IOError: + except OSError: pass @@ -231,7 +229,7 @@ def run(self): else: class bdist_wheel(wheel.bdist_wheel.bdist_wheel): def finalize_options(self): - self.py_limited_api = 'cp3{}'.format(sys.version_info[1]) + self.py_limited_api = f'cp3{sys.version_info[1]}' super().finalize_options() cmdclass['bdist_wheel'] = bdist_wheel @@ -270,7 +268,6 @@ def finalize_options(self): ['sassc = sassc:main'], ], }, - install_requires=['six'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', @@ -279,7 +276,6 @@ def finalize_options(self): 'Operating System :: OS Independent', 'Programming Language :: C', 'Programming Language :: C++', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', @@ -293,5 +289,6 @@ def finalize_options(self): 'Topic :: Software Development :: Code Generators', 'Topic :: Software Development :: Compilers', ], + python_requires='>=3.6', cmdclass=cmdclass, ) diff --git a/tox.ini b/tox.ini index 38047b9b..f4b84ad0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pypy,pypy3,py27,py36,py37,py38,py39,pre-commit +envlist = pypy3,py36,py37,py38,py39,pre-commit [testenv] usedevelop = true pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy