diff --git a/pysass.cpp b/pysass.cpp index 3e36c0b6..6cfdfb72 100644 --- a/pysass.cpp +++ b/pysass.cpp @@ -4,9 +4,11 @@ #if PY_MAJOR_VERSION >= 3 #define PySass_IF_PY3(three, two) (three) #define PySass_Object_Bytes(o) PyUnicode_AsUTF8String(PyObject_Str(o)) +#define COLLECTIONS_ABC_MOD "collections.abc" #else #define PySass_IF_PY3(three, two) (two) #define PySass_Object_Bytes(o) PyObject_Str(o) +#define COLLECTIONS_ABC_MOD "collections" #endif #ifdef __cplusplus @@ -322,7 +324,7 @@ static union Sass_Value* _to_sass_value(PyObject* value) { PyObject* sass_list_t = PyObject_GetAttrString(types_mod, "SassList"); PyObject* sass_warning_t = PyObject_GetAttrString(types_mod, "SassWarning"); PyObject* sass_error_t = PyObject_GetAttrString(types_mod, "SassError"); - PyObject* collections_mod = PyImport_ImportModule("collections"); + PyObject* collections_mod = PyImport_ImportModule(COLLECTIONS_ABC_MOD); PyObject* mapping_t = PyObject_GetAttrString(collections_mod, "Mapping"); if (value == Py_None) { diff --git a/sass.py b/sass.py index 2590197b..7413df2e 100644 --- a/sass.py +++ b/sass.py @@ -25,6 +25,7 @@ from six import string_types, text_type, PY2, PY3 import _sass +from sassutils._compat import collections_abc __all__ = ( 'MODES', 'OUTPUT_STYLES', 'SOURCE_COMMENTS', 'CompileError', 'SassColor', @@ -144,7 +145,7 @@ def from_named_function(cls, function): def __init__(self, name, arguments, callable_): if not isinstance(name, string_types): raise TypeError('name must be a string, not ' + repr(name)) - elif not isinstance(arguments, collections.Sequence): + elif not isinstance(arguments, collections_abc.Sequence): raise TypeError('arguments must be a sequence, not ' + repr(arguments)) elif not callable(callable_): @@ -582,12 +583,15 @@ def _get_file_arg(key): include_paths = include_paths.encode(fs_encoding) custom_functions = kwargs.pop('custom_functions', ()) - if isinstance(custom_functions, collections.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.Set, collections.Sequence)): + elif isinstance( + custom_functions, + (collections_abc.Set, collections_abc.Sequence), + ): custom_functions = [ func if isinstance(func, SassFunction) else SassFunction.from_named_function(func) @@ -758,7 +762,7 @@ def __new__(cls, msg): return super(SassWarning, cls).__new__(cls, msg) -class SassMap(collections.Mapping): +class SassMap(collections_abc.Mapping): """Because sass maps can have mapping types as keys, we need an immutable hashable mapping type. diff --git a/sasstests.py b/sasstests.py index 84480637..597e539e 100644 --- a/sasstests.py +++ b/sasstests.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import with_statement -import collections import contextlib import glob import json @@ -25,6 +24,7 @@ import pysassc import sass import sassc +from sassutils._compat import collections_abc from sassutils.builder import Manifest, build_directory from sassutils.wsgi import SassMiddleware @@ -127,6 +127,12 @@ def normalize_path(path): ''' +@pytest.fixture(autouse=True) +def no_warnings(recwarn): + yield + assert len(recwarn) == 0 + + class BaseTestCase(unittest.TestCase): def assert_source_map_equal(self, expected, actual): @@ -153,7 +159,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.Mapping) + assert isinstance(sass.OUTPUT_STYLES, collections_abc.Mapping) assert 'nested' in sass.OUTPUT_STYLES def test_and_join(self): @@ -926,8 +932,10 @@ def test_successful(self): assert os.path.exists(os.path.join(output_dir, 'foo/f2.css')) assert not os.path.exists(os.path.join(output_dir, 'baz.txt')) - contentsf1 = open(os.path.join(output_dir, 'f1.css')).read() - contentsf2 = open(os.path.join(output_dir, 'foo/f2.css')).read() + with open(os.path.join(output_dir, 'f1.css')) as f: + contentsf1 = f.read() + with open(os.path.join(output_dir, 'foo/f2.css')) as f: + contentsf2 = f.read() assert contentsf1 == 'a b {\n width: 100%; }\n' assert contentsf2 == 'foo {\n width: 100%; }\n' diff --git a/sassutils/_compat.py b/sassutils/_compat.py new file mode 100644 index 00000000..8f794ac8 --- /dev/null +++ b/sassutils/_compat.py @@ -0,0 +1,7 @@ +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 e775e718..33060f57 100644 --- a/sassutils/builder.py +++ b/sassutils/builder.py @@ -4,7 +4,6 @@ """ from __future__ import with_statement -import collections import io import os import os.path @@ -14,6 +13,7 @@ from six import string_types from sass import compile +from sassutils._compat import collections_abc __all__ = 'SUFFIXES', 'SUFFIX_PATTERN', 'Manifest', 'build_directory' @@ -104,7 +104,7 @@ class Manifest(object): def normalize_manifests(cls, manifests): if manifests is None: manifests = {} - elif isinstance(manifests, collections.Mapping): + elif isinstance(manifests, collections_abc.Mapping): manifests = dict(manifests) else: raise TypeError('manifests must be a mapping object, not ' + @@ -117,7 +117,7 @@ def normalize_manifests(cls, manifests): continue elif isinstance(manifest, tuple): manifest = Manifest(*manifest) - elif isinstance(manifest, collections.Mapping): + elif isinstance(manifest, collections_abc.Mapping): manifest = Manifest(**manifest) elif isinstance(manifest, string_types): manifest = Manifest(manifest) diff --git a/sassutils/wsgi.py b/sassutils/wsgi.py index 408220be..4ae75ebe 100644 --- a/sassutils/wsgi.py +++ b/sassutils/wsgi.py @@ -4,7 +4,6 @@ """ from __future__ import absolute_import, with_statement -import collections import logging import os import os.path @@ -12,6 +11,7 @@ from pkg_resources import resource_filename from sass import CompileError +from sassutils._compat import collections_abc from .builder import Manifest __all__ = 'SassMiddleware', @@ -98,7 +98,7 @@ def __init__( 'not ' + repr(app)) self.app = app self.manifests = Manifest.normalize_manifests(manifests) - if not isinstance(package_dir, collections.Mapping): + if not isinstance(package_dir, collections_abc.Mapping): raise TypeError('package_dir must be a mapping object, not ' + repr(package_dir)) self.error_status = error_status diff --git a/tox.ini b/tox.ini index 43ebbf00..5fb4d430 100644 --- a/tox.ini +++ b/tox.ini @@ -5,4 +5,4 @@ envlist = pypy, pypy3, py27, py35, py36, py37 deps = -rrequirements-dev.txt commands = pytest sasstests.py - flake8 . + pre-commit run --all-files