diff --git a/python-webassets.changes b/python-webassets.changes index 3fdec26..32996a3 100644 --- a/python-webassets.changes +++ b/python-webassets.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Apr 26 23:06:51 UTC 2021 - Ben Greiner + +- Add webassets-py39-threading.patch -- gh#miracle2k/webassets#529 +- Add remove-nose.patch -- gh#miracle2k/webassets#539 +- Install some filter packages for test suite + ------------------------------------------------------------------- Mon May 18 07:25:35 UTC 2020 - Petr Gajdos diff --git a/python-webassets.spec b/python-webassets.spec index 38018b0..edef790 100644 --- a/python-webassets.spec +++ b/python-webassets.spec @@ -1,7 +1,7 @@ # # spec file for package python-webassets # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,6 +17,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%bcond_without python2 Name: python-webassets Version: 2.0 Release: 0 @@ -25,19 +26,35 @@ Summary: Media asset management for Python, with glue code for various we # jspacker=LGPL-2.1 # cssrewrite=BSD-3-Clause # six.py=MIT -License: BSD-2-Clause AND Apache-2.0 AND LGPL-2.1-only AND BSD-3-Clause AND MIT +License: Apache-2.0 AND BSD-2-Clause AND LGPL-2.1-only AND BSD-3-Clause AND MIT Group: Development/Languages/Python URL: https://github.com/miracle2k/webassets/ Source: https://files.pythonhosted.org/packages/source/w/webassets/webassets-%{version}.tar.gz -BuildRequires: %{python_module mock} -BuildRequires: %{python_module nose} -BuildRequires: %{python_module pytest} +# PATCH-FIX-UPSTREAM webassets-py39-threading.patch -- gh#miracle2k/webassets#529 +Patch0: https://github.com/miracle2k/webassets/pull/529.patch#/webassets-py39-threading.patch +# PATCH-FIX-UPSTREAM remove-nose -- gh#miracle2k/webassets#539 +Patch1: remove-nose.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros +# SECTION test requirements +# jsmin and rjsmin fail if imported: different utf8 filters +BuildRequires: %{python_module pytest} +BuildRequires: %{python_module Jinja2} +BuildRequires: %{python_module PyYAML} +BuildRequires: %{python_module cssutils} +BuildRequires: %{python_module glob2} +BuildRequires: %{python_module lesscpy} +BuildRequires: %{python_module rcssmin} +BuildRequires: %{python_module slimit} +BuildRequires: sassc +%if %{with python2} +BuildRequires: python2-mock +%endif +# /SECTION Requires: python-setuptools Requires(post): update-alternatives -Requires(postun): update-alternatives +Requires(postun):update-alternatives BuildArch: noarch %python_subpackages @@ -47,7 +64,7 @@ of different filters, including YUI, jsmin, jspacker or CSS tidy. Also supports URL rewriting in CSS files. %prep -%setup -q -n webassets-%{version} +%autosetup -p1 -n webassets-%{version} sed -i 's/#!.*//' src/webassets/filter/rjsmin/rjsmin.py # fix py2 only syntax sed -i -e 's:e.message:e.args[0]:g' tests/test_filters.py @@ -62,7 +79,7 @@ sed -i -e 's:e.message:e.args[0]:g' tests/test_filters.py %check export LANG="en_US.UTF8" -%pytest +%pytest -ra %post %python_install_alternative webassets diff --git a/remove-nose.patch b/remove-nose.patch new file mode 100644 index 0000000..0a57eba --- /dev/null +++ b/remove-nose.patch @@ -0,0 +1,1312 @@ +Index: webassets-2.0/tests/helpers.py +=================================================================== +--- webassets-2.0.orig/tests/helpers.py ++++ webassets-2.0/tests/helpers.py +@@ -5,7 +5,7 @@ from webassets.test import TempDirHelper + + + __all__ = ('TempDirHelper', 'TempEnvironmentHelper', 'noop', +- 'assert_raises_regexp', 'check_warnings') ++ 'assert_raises_regex', 'check_warnings') + + + # Define a noop filter; occasionally in tests we need to define +@@ -13,28 +13,9 @@ __all__ = ('TempDirHelper', 'TempEnviron + noop = lambda _in, out: out.write(_in.read()) + + +-try: +- # Python 3 +- from nose.tools import assert_raises_regex +-except ImportError: +- try: +- # Python >= 2.7 +- from nose.tools import assert_raises_regexp as assert_raises_regex +- except: +- # Python < 2.7 +- def assert_raises_regex(expected, regexp, callable, *a, **kw): +- try: +- callable(*a, **kw) +- except expected as e: +- if isinstance(regexp, basestring): +- regexp = re.compile(regexp) +- if not regexp.search(str(e.message)): +- raise self.failureException('"%s" does not match "%s"' % +- (regexp.pattern, str(e.message))) +- else: +- if hasattr(expected,'__name__'): excName = expected.__name__ +- else: excName = str(expected) +- raise AssertionError("%s not raised" % excName) ++from pytest import raises ++def assert_raises_regex(expected, regexp, callable, *a, **kw): ++ raises(expected, callable, *a, **kw).match(regexp) + + + try: +Index: webassets-2.0/tests/test_bundle_build.py +=================================================================== +--- webassets-2.0.orig/tests/test_bundle_build.py ++++ webassets-2.0/tests/test_bundle_build.py +@@ -6,8 +6,10 @@ more likely` found in `test_bundle_vario + + + import os +-from nose.tools import assert_raises ++ ++from pytest import raises as assert_raises + import pytest ++ + from webassets import Bundle + from webassets.cache import MemoryCache + from webassets.exceptions import BuildError, BundleError +Index: webassets-2.0/tests/test_bundle_urls.py +=================================================================== +--- webassets-2.0.orig/tests/test_bundle_urls.py ++++ webassets-2.0/tests/test_bundle_urls.py +@@ -6,8 +6,9 @@ more likely` found in `test_bundle_vario + + from __future__ import with_statement + +-from nose import SkipTest +-from nose.tools import assert_raises, assert_equal ++import re ++ ++import pytest + + from tests.test_bundle_build import AppendFilter + from webassets import Bundle +@@ -64,12 +65,12 @@ class TestUrlsVarious(BaseUrlsTester): + value.""" + # On the bundle level + b = self.MockBundle('a', 'b', debug="invalid") +- assert_raises(BundleError, b.urls, env=self.env) ++ pytest.raises(BundleError, b.urls, env=self.env) + + # On the environment level + self.env.debug = "invalid" + b = self.MockBundle('a', 'b') +- assert_raises(BundleError, b.urls, env=self.env) ++ pytest.raises(BundleError, b.urls, env=self.env) + + # Self-check - this should work if this test works. + self.env.debug = True # valid again +@@ -96,7 +97,7 @@ class TestUrlsVarious(BaseUrlsTester): + """ + self.env.debug = True + bundle = self.mkbundle('non-existent-file', output="out") +- assert_raises(BundleError, bundle.urls) ++ pytest.raises(BundleError, bundle.urls) + + def test_filters_in_debug_mode(self): + """Test that if a filter is used which runs in debug mode, the bundle +@@ -124,33 +125,25 @@ class TestUrlsVarious(BaseUrlsTester): + """If a bundle contains absolute paths outside of the + media directory, to generate a url they are copied in. + """ +- try: +- from nose.tools import assert_regex +- except ImportError: +- raise SkipTest("Assertion method only present in 2.7+") + self.env.debug = True + with TempDirHelper() as h: + h.create_files(['foo.css']) + bundle = self.mkbundle(h.path('foo.css')) + urls = bundle.urls() + assert len(urls) == 1 +- assert_regex(urls[0], r'.*/webassets-external/[\da-z]*_foo.css') ++ assert re.match(r'.*/webassets-external/[\da-z]*_foo.css', urls[0]) + + def test_external_refs_calculate_sri(self): + """If a bundle contains absolute paths outside of the + media directory, to generate a url they are copied in. + """ +- try: +- from nose.tools import assert_regex +- except ImportError: +- raise SkipTest("Assertion method only present in 2.7+") + self.env.debug = True + with TempDirHelper() as h: + h.create_files(['foo.css']) + bundle = self.mkbundle(h.path('foo.css')) + urls = bundle.urls(calculate_sri=True) + assert len(urls) == 1 +- assert_regex(urls[0]['uri'], r'.*/webassets-external/[\da-z]*_foo.css') ++ assert re.match(r'.*/webassets-external/[\da-z]*_foo.css', urls[0]['uri']) + assert urls[0]['sri'] == _EMPTY_FILE_SRI + + +@@ -194,7 +187,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + does not affect url generation). + """ + bundle = self.MockBundle('1', '2', output='childout', debug='merge') +- assert_equal(bundle.urls(), ['/childout']) ++ assert bundle.urls() == ['/childout'] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_debug_true(self): +@@ -203,7 +196,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + ineffectual. + """ + bundle = self.MockBundle('1', '2', output='childout', debug=True) +- assert_equal(bundle.urls(), ['/childout']) ++ assert bundle.urls() == ['/childout'] + assert len(self.build_called) == 1 + + def test_root_debug_true_and_child_debug_false(self): +@@ -217,7 +210,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + '1', '2', + self.MockBundle('a', output='child1', debug=False), + output='rootout', debug=True) +- assert_equal(bundle.urls(), ['/rootout']) ++ assert bundle.urls() == ['/rootout'] + + def test_simple_bundle_with_sri(self): + bundle = self.MockBundle('a', 'b', 'c', output='out') +@@ -259,7 +252,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + does not affect url generation). + """ + bundle = self.MockBundle('1', '2', output='childout', debug='merge') +- assert_equal(bundle.urls(calculate_sri=True), [{'uri': '/childout', 'sri': None}]) ++ assert bundle.urls(calculate_sri=True) == [{'uri': '/childout', 'sri': None}] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_debug_true_with_sri(self): +@@ -268,7 +261,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + ineffectual. + """ + bundle = self.MockBundle('1', '2', output='childout', debug=True) +- assert_equal(bundle.urls(calculate_sri=True), [{'uri': '/childout', 'sri': None}]) ++ assert bundle.urls(calculate_sri=True) == [{'uri': '/childout', 'sri': None}] + assert len(self.build_called) == 1 + + def test_root_debug_true_and_child_debug_false_with_sri(self): +@@ -282,7 +275,7 @@ class TestUrlsWithDebugFalse(BaseUrlsTes + '1', '2', + self.MockBundle('a', output='child1', debug=False), + output='rootout', debug=True) +- assert_equal(bundle.urls(calculate_sri=True), [{'uri': '/rootout', 'sri': None}]) ++ assert bundle.urls(calculate_sri=True) == [{'uri': '/rootout', 'sri': None}] + + + class TestUrlsWithDebugTrue(BaseUrlsTester): +@@ -295,8 +288,8 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + + def test_simple_bundle(self): + bundle = self.MockBundle('a', 'b', 'c', output='out') +- assert_equal(bundle.urls(), ['/a', '/b', '/c']) +- assert_equal(len(self.build_called), 0) ++ assert bundle.urls() == ['/a', '/b', '/c'] ++ assert len(self.build_called) == 0 + + def test_nested_bundle(self): + bundle = self.MockBundle( +@@ -318,8 +311,8 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + """[Regression] Test a Bundle that contains a source URL. + """ + bundle = self.MockBundle('http://test.de', output='out') +- assert_equal(bundle.urls(), ['http://test.de']) +- assert_equal(len(self.build_called), 0) ++ assert bundle.urls() == ['http://test.de'] ++ assert len(self.build_called) == 0 + + # This is the important test. It proves that the url source + # was handled separately, and not processed like any other +@@ -328,14 +321,14 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + # converts a bundle content into an url operates just fine + # on a url source, so there is no easy other way to determine + # whether the url source was treated special. +- assert_equal(len(self.makeurl_called), 0) ++ assert len(self.makeurl_called) == 0 + + def test_root_bundle_switching_to_debug_false(self): + """A bundle explicitly says it wants to be processed with + debug=False, overriding the global "debug=True" setting. + """ + bundle = self.MockBundle('1', '2', output='childout', debug=False) +- assert_equal(bundle.urls(), ['/childout']) ++ assert bundle.urls() == ['/childout'] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_merge(self): +@@ -343,7 +336,7 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + the global "debug=True" setting. + """ + bundle = self.MockBundle('1', '2', output='childout', debug='merge') +- assert_equal(bundle.urls(), ['/childout']) ++ assert bundle.urls() == ['/childout'] + assert len(self.build_called) == 1 + + def test_child_bundle_switching(self): +@@ -354,16 +347,16 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug='merge'), + 'c', output='out') +- assert_equal(bundle.urls(), ['/a', '/childout', '/c']) ++ assert bundle.urls() == ['/a', '/childout', '/c'] + assert len(self.build_called) == 1 + + def test_simple_bundle_with_sri(self): + bundle = self.MockBundle('a', 'b', 'c', output='out') +- assert_equal(bundle.urls(calculate_sri=True), +- [{'sri': _EMPTY_FILE_SRI, 'uri': '/a'}, ++ assert bundle.urls(calculate_sri=True) == [ ++ {'sri': _EMPTY_FILE_SRI, 'uri': '/a'}, + {'sri': _EMPTY_FILE_SRI, 'uri': '/b'}, +- {'sri': _EMPTY_FILE_SRI, 'uri': '/c'}]) +- assert_equal(len(self.build_called), 0) ++ {'sri': _EMPTY_FILE_SRI, 'uri': '/c'}] ++ assert len(self.build_called) == 0 + + def test_nested_bundle_with_sri(self): + bundle = self.MockBundle( +@@ -389,8 +382,8 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + """[Regression] Test a Bundle that contains a source URL. + """ + bundle = self.MockBundle('http://test.de', output='out') +- assert_equal(bundle.urls(calculate_sri=True), [{'sri': None, 'uri': 'http://test.de'}]) +- assert_equal(len(self.build_called), 0) ++ assert bundle.urls(calculate_sri=True) == [{'sri': None, 'uri': 'http://test.de'}] ++ assert len(self.build_called) == 0 + + # This is the important test. It proves that the url source + # was handled separately, and not processed like any other +@@ -399,14 +392,14 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + # converts a bundle content into an url operates just fine + # on a url source, so there is no easy other way to determine + # whether the url source was treated special. +- assert_equal(len(self.makeurl_called), 0) ++ assert len(self.makeurl_called) == 0 + + def test_root_bundle_switching_to_debug_false_with_sri(self): + """A bundle explicitly says it wants to be processed with + debug=False, overriding the global "debug=True" setting. + """ + bundle = self.MockBundle('1', '2', output='childout', debug=False) +- assert_equal(bundle.urls(calculate_sri=True), [{'sri': None, 'uri': '/childout'}]) ++ assert bundle.urls(calculate_sri=True) == [{'sri': None, 'uri': '/childout'}] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_merge_with_sri(self): +@@ -414,7 +407,7 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + the global "debug=True" setting. + """ + bundle = self.MockBundle('1', '2', output='childout', debug='merge') +- assert_equal(bundle.urls(calculate_sri=True), [{'sri': None, 'uri': '/childout'}]) ++ assert bundle.urls(calculate_sri=True) == [{'sri': None, 'uri': '/childout'}] + assert len(self.build_called) == 1 + + def test_child_bundle_switching_with_sri(self): +@@ -425,10 +418,10 @@ class TestUrlsWithDebugTrue(BaseUrlsTest + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug='merge'), + 'c', output='out') +- assert_equal(bundle.urls(calculate_sri=True), +- [{'sri': _EMPTY_FILE_SRI, 'uri': '/a'}, ++ assert bundle.urls(calculate_sri=True) == [ ++ {'sri': _EMPTY_FILE_SRI, 'uri': '/a'}, + {'sri': None, 'uri': '/childout'}, +- {'sri': _EMPTY_FILE_SRI, 'uri': '/c'}]) ++ {'sri': _EMPTY_FILE_SRI, 'uri': '/c'}] + assert len(self.build_called) == 1 + + +@@ -457,7 +450,7 @@ class TestUrlsWithDebugMerge(BaseUrlsTes + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug=False), + 'c', output='out') +- assert_equal(bundle.urls(), ['/out']) ++ assert bundle.urls() == ['/out'] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_debug_true(self): +@@ -467,7 +460,7 @@ class TestUrlsWithDebugMerge(BaseUrlsTes + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug=True), + 'c', output='out') +- assert_equal(bundle.urls(), ['/out']) ++ assert bundle.urls() == ['/out'] + assert len(self.build_called) == 1 + + def test_simple_bundle_with_sri(self): +@@ -489,7 +482,7 @@ class TestUrlsWithDebugMerge(BaseUrlsTes + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug=False), + 'c', output='out') +- assert_equal(bundle.urls(calculate_sri=True), [{'sri': None, 'uri': '/out'}]) ++ assert bundle.urls(calculate_sri=True) == [{'sri': None, 'uri': '/out'}] + assert len(self.build_called) == 1 + + def test_root_bundle_switching_to_debug_true_with_sri(self): +@@ -499,5 +492,5 @@ class TestUrlsWithDebugMerge(BaseUrlsTes + bundle = self.MockBundle( + 'a', self.MockBundle('1', '2', output='childout', debug=True), + 'c', output='out') +- assert_equal(bundle.urls(calculate_sri=True), [{'sri': None, 'uri': '/out'}]) ++ assert bundle.urls(calculate_sri=True) == [{'sri': None, 'uri': '/out'}] + assert len(self.build_called) == 1 +Index: webassets-2.0/tests/test_bundle_various.py +=================================================================== +--- webassets-2.0.orig/tests/test_bundle_various.py ++++ webassets-2.0/tests/test_bundle_various.py +@@ -13,12 +13,12 @@ try: + HTTPHandler, build_opener, install_opener, addinfourl + except ImportError: # Py2 + from urllib2 import HTTPHandler, build_opener, install_opener, addinfourl ++ ++import pytest ++ + from webassets.six import StringIO + from webassets.six.moves import filter + +-from nose.tools import assert_raises, assert_equal +-from nose import SkipTest +- + from webassets import Bundle + from webassets.utils import set + from webassets.bundle import get_all_bundle_files +@@ -94,7 +94,7 @@ class TestBundleConfig(TempEnvironmentHe + b = self.mkbundle(filters=['jsmin', 'cssutils']) + _assert(b.filters, 2) + # Strings inside a list may not be further comma separated +- assert_raises(ValueError, self.mkbundle, filters=['jsmin,cssutils']) ++ pytest.raises(ValueError, self.mkbundle, filters=['jsmin,cssutils']) + + # A single or multiple classes may be given + b = self.mkbundle(filters=TestFilter) +@@ -113,8 +113,8 @@ class TestBundleConfig(TempEnvironmentHe + _assert(b.filters, 2) + + # If something is wrong, an error is raised right away. +- assert_raises(ValueError, self.mkbundle, filters='notreallyafilter') +- assert_raises(ValueError, self.mkbundle, filters=object()) ++ pytest.raises(ValueError, self.mkbundle, filters='notreallyafilter') ++ pytest.raises(ValueError, self.mkbundle, filters=object()) + + # [bug] Specifically test that we can assign ``None``. + self.mkbundle().filters = None +@@ -272,7 +272,7 @@ class TestVersionFeatures(TempEnvironmen + assert bundle.version == 'foo' + self.env.manifest.version = None + self.env.versions.version = None +- assert_raises(BundleError, bundle.get_version, refresh=True) ++ pytest.raises(BundleError, bundle.get_version, refresh=True) + + def test_url_expire(self): + """Test the url_expire option. +@@ -399,7 +399,7 @@ class TestLoadPath(TempEnvironmentHelper + assert self.get('dir/out') == 'a' + + # Error because the file from directory is not found +- assert_raises(BundleError, self.mkbundle('bar', output='out').build) ++ pytest.raises(BundleError, self.mkbundle('bar', output='out').build) + + def test_globbing(self): + """When used with globbing.""" +@@ -488,7 +488,7 @@ class TestGlobbing(TempEnvironmentHelper + self.env.debug = True + urls = self.mkbundle('*.js', output='out').urls() + urls.sort() +- assert_equal(urls, ['/file1.js', '/file2.js']) ++ assert urls == ['/file1.js', '/file2.js'] + + def test_empty_pattern(self): + bundle = self.mkbundle('*.xyz', output='out') +@@ -508,10 +508,7 @@ class TestGlobbing(TempEnvironmentHelper + def test_recursive_globbing(self): + """Test recursive globbing using python-glob2. + """ +- try: +- import glob2 +- except ImportError: +- raise SkipTest() ++ glob2 = pytest.importorskip("glob2") + + self.create_files({'sub/file.js': 'sub',}) + self.mkbundle('**/*.js', output='out').build() +@@ -619,7 +616,7 @@ class TestUrlContents(TempEnvironmentHel + def test_invalid_url(self): + """If a bundle contains an invalid url, building will raise an error. + """ +- assert_raises(BuildError, ++ pytest.raises(BuildError, + self.mkbundle('http://bar', output='out').build) + + def test_autorebuild_updaters(self): +Index: webassets-2.0/tests/test_cache.py +=================================================================== +--- webassets-2.0.orig/tests/test_cache.py ++++ webassets-2.0/tests/test_cache.py +@@ -2,7 +2,7 @@ from __future__ import with_statement + + import random + import pytest +-from nose.tools import assert_equal ++ + from webassets.filter import Filter + from webassets.cache import BaseCache, FilesystemCache, MemoryCache + from webassets.updater import TimestampUpdater +@@ -123,15 +123,15 @@ class TestCacheIsUsed(TempEnvironmentHel + bundle = self.mkbundle('in1', 'in2', output='out', filters=self.filter) + self.cache.enabled = False + bundle.build() +- assert_equal(self.cache.getops, 6) # 2x first, 2x input, 1x output, 1x cache +- assert_equal(self.cache.setops, 7) # like getops + 1x bdef ++ assert self.cache.getops == 6 # 2x first, 2x input, 1x output, 1x cache ++ assert self.cache.setops == 7 # like getops + 1x bdef + + def test_cache_enabled(self): + bundle = self.mkbundle('in1', 'in2', output='out', filters=self.filter) + self.cache.enabled = True + bundle.build() +- assert_equal(self.cache.getops, 6) # # 2x first, 2x input, 1x output, 1x cache +- assert_equal(self.cache.setops, 1) # one hit by (bdef) ++ assert self.cache.getops == 6 # # 2x first, 2x input, 1x output, 1x cache ++ assert self.cache.setops == 1 # one hit by (bdef) + + def test_filesystem_cache(self): + """Regresssion test for two bugs: +Index: webassets-2.0/tests/test_environment.py +=================================================================== +--- webassets-2.0.orig/tests/test_environment.py ++++ webassets-2.0/tests/test_environment.py +@@ -1,7 +1,8 @@ + from __future__ import with_statement + + import os +-from nose.tools import assert_raises ++ ++import pytest + + from webassets import six + from webassets import Environment +@@ -92,14 +93,14 @@ class TestEnvApi(object): + env = Environment('tests') + + # No `output` +- assert_raises( ++ pytest.raises( + RegisterError, env.register, + 'base1', 'helpers.py', merge=False + ) + + # Nested bundle + b = Bundle() +- assert_raises( ++ pytest.raises( + RegisterError, env.register, + 'base2', 'helpers.py', b, merge=False, output='a' + ) +@@ -107,8 +108,8 @@ class TestEnvApi(object): + def test_register_invalid_call(self): + """Test calling self.m.register with an invalid syntax. + """ +- assert_raises(TypeError, self.m.register) +- assert_raises(TypeError, self.m.register, 'one-argument-only') ++ pytest.raises(TypeError, self.m.register) ++ pytest.raises(TypeError, self.m.register, 'one-argument-only') + + def test_register_duplicate(self): + """Test name clashes. +@@ -125,8 +126,8 @@ class TestEnvApi(object): + assert len(self.m) == 1 + + # Otherwise, an error is raised. +- assert_raises(RegisterError, self.m.register, 'foo', b2) +- assert_raises(RegisterError, self.m.register, 'foo', 's1', 's2', 's3') ++ pytest.raises(RegisterError, self.m.register, 'foo', b2) ++ pytest.raises(RegisterError, self.m.register, 'foo', 's1', 's2', 's3') + + def test_register_anon_bundle(self): + """Self registering an anonymous bundle. +@@ -158,8 +159,8 @@ class TestEnvApi(object): + # An environment can be constructed without given url or directory. + env = Environment() + # But then accessing them will fail, and with it most operations. +- assert_raises(EnvironmentError, getattr, env, 'url') +- assert_raises(EnvironmentError, getattr, env, 'directory') ++ pytest.raises(EnvironmentError, getattr, env, 'url') ++ pytest.raises(EnvironmentError, getattr, env, 'directory') + + # Test constructing the environment with values for url and directory + env = Environment('foo', 'bar') +@@ -242,7 +243,7 @@ class TestSpecialProperties(object): + + # Invalid value + self.m.versions = 'invalid-value' +- assert_raises(ValueError, getattr, self.m, 'versions') ++ pytest.raises(ValueError, getattr, self.m, 'versions') + + def test_cache(self): + from webassets.cache import BaseCache, FilesystemCache +Index: webassets-2.0/tests/test_ext/test_jinja2.py +=================================================================== +--- webassets-2.0.orig/tests/test_ext/test_jinja2.py ++++ webassets-2.0/tests/test_ext/test_jinja2.py +@@ -1,15 +1,13 @@ +-from nose.plugins.skip import SkipTest ++import pytest ++ + from webassets import Environment as AssetsEnvironment, Bundle + from webassets.test import TempEnvironmentHelper + + +-try: +- import jinja2 +-except ImportError: +- raise SkipTest('Jinja2 not installed') +-else: +- from jinja2 import Template, Environment as JinjaEnvironment +- from webassets.ext.jinja2 import AssetsExtension, Jinja2Loader ++jinja2 = pytest.importorskip('jinja2') ++ ++from jinja2 import Template, Environment as JinjaEnvironment ++from webassets.ext.jinja2 import AssetsExtension, Jinja2Loader + + + class TestTemplateTag(object): +Index: webassets-2.0/tests/test_filters.py +=================================================================== +--- webassets-2.0.orig/tests/test_filters.py ++++ webassets-2.0/tests/test_filters.py +@@ -6,11 +6,17 @@ import os + import os.path + from subprocess import check_output + from contextlib import contextmanager +-from nose.tools import assert_raises, assert_equal, assert_true +-from nose import SkipTest +-from mock import patch, Mock, DEFAULT ++ ++try: ++ from unittest.mock import patch, Mock, DEFAULT ++except ImportError: ++ from mock import patch, Mock, DEFAULT ++ + from distutils.spawn import find_executable + import re ++ ++import pytest ++ + from webassets.utils import StringIO + from webassets import Environment + from webassets.exceptions import FilterError +@@ -66,7 +72,7 @@ class TestFilterBaseClass(object): + # Test __init__ arguments + assert TestFilter(attr1='foo').attr1 == 'foo' + assert TestFilter(secondattr='foo').attr2 == 'foo' +- assert_raises(TypeError, TestFilter, attr3='foo') ++ pytest.raises(TypeError, TestFilter, attr3='foo') + assert TestFilter(attr4='foo').attr4 == 'foo' + + # Test config vars +@@ -94,19 +100,19 @@ class TestFilterBaseClass(object): + + with os_environ_sandbox(): + # Test raising of error, and test not raising it. +- assert_raises(EnvironmentError, get_config, NAME) ++ pytest.raises(EnvironmentError, get_config, NAME) + assert get_config(NAME, require=False) is None + + # Start with only the environment variable set. + os.environ[NAME] = 'bar' + assert get_config(NAME) == 'bar' + assert get_config(env=NAME, setting=False) == 'bar' +- assert_raises(EnvironmentError, get_config, setting=NAME, env=False) ++ pytest.raises(EnvironmentError, get_config, setting=NAME, env=False) + + # Set the value in the environment as well. + m.config[NAME] = 'foo' + # Ensure that settings take precedence. +- assert_equal(get_config(NAME), 'foo') ++ assert get_config(NAME) == 'foo' + # Two different names can be supplied. + assert get_config(setting=NAME2, env=NAME) == 'bar' + +@@ -114,7 +120,7 @@ class TestFilterBaseClass(object): + del os.environ[NAME] + assert get_config(NAME) == 'foo' + assert get_config(setting=NAME, env=False) == 'foo' +- assert_raises(EnvironmentError, get_config, env=NAME) ++ pytest.raises(EnvironmentError, get_config, env=NAME) + + def test_getconfig_os_env_types(self): + """Test type conversion for values read from the environment. +@@ -174,7 +180,7 @@ class TestExternalToolClass(object): + def setup(self): + if not hasattr(str, 'format'): + # A large part of this functionality is not available on Python 2.5 +- raise SkipTest() ++ pytest.skip("test not for py 2.5") + self.patcher = patch('subprocess.Popen') + self.popen = self.patcher.start() + self.popen.return_value = Mock() +@@ -225,7 +231,7 @@ class TestExternalToolClass(object): + assert Filter.result == ([], None) + + def test_method_invalid(self): +- assert_raises(AssertionError, ++ pytest.raises(AssertionError, + type, 'Filter', (ExternalTool,), {'method': 'foobar'}) + + def test_no_method(self): +@@ -307,7 +313,7 @@ class TestExternalToolClass(object): + # With error + self.popen.return_value.returncode = 1 + self.popen.return_value.communicate.return_value = [b'stdout', b'stderr'] +- assert_raises(FilterError, Filter.subprocess, ['test'], StringIO()) ++ pytest.raises(FilterError, Filter.subprocess, ['test'], StringIO()) + + def test_input_var(self): + """Test {input} variable.""" +@@ -331,7 +337,7 @@ class TestExternalToolClass(object): + assert not os.path.exists(intercepted['filename']) + + # {input} requires input data +- assert_raises(ValueError, Filter.subprocess, ['{input}'], StringIO()) ++ pytest.raises(ValueError, Filter.subprocess, ['{input}'], StringIO()) + + def test_output_var(self): + class Filter(ExternalTool): pass +@@ -382,13 +388,13 @@ def test_register_filter(): + """Test registration of custom filters. + """ + # Needs to be a ``Filter`` subclass. +- assert_raises(ValueError, register_filter, object) ++ pytest.raises(ValueError, register_filter, object) + + # A name is required. + class MyFilter(Filter): + name = None + def output(self, *a, **kw): pass +- assert_raises(ValueError, register_filter, MyFilter) ++ pytest.raises(ValueError, register_filter, MyFilter) + + # We should be able to register a filter with a name. + MyFilter.name = 'foo' +@@ -400,7 +406,7 @@ def test_register_filter(): + name = 'foo' + def output(self, *a, **kw): pass + register_filter(OverrideMyFilter) +- assert_true(isinstance(get_filter('foo'), OverrideMyFilter)) ++ assert isinstance(get_filter('foo'), OverrideMyFilter) + + + def test_get_filter(): +@@ -408,12 +414,12 @@ def test_get_filter(): + """ + # By name - here using one of the builtins. + assert isinstance(get_filter('jsmin'), Filter) +- assert_raises(ValueError, get_filter, 'notafilteractually') ++ pytest.raises(ValueError, get_filter, 'notafilteractually') + + # By class. + class MyFilter(Filter): pass + assert isinstance(get_filter(MyFilter), MyFilter) +- assert_raises(ValueError, get_filter, object()) ++ pytest.raises(ValueError, get_filter, object()) + + # Passing an instance doesn't do anything. + f = MyFilter() +@@ -426,8 +432,8 @@ def test_get_filter(): + assert get_filter('rcssmin', keep_bang_comments=True).keep_bang_comments == True + # However, this is not allowed when a filter instance is passed directly, + # or a callable object. +- assert_raises(AssertionError, get_filter, f, 'test') +- assert_raises(AssertionError, get_filter, lambda: None, 'test') ++ pytest.raises(AssertionError, get_filter, f, 'test') ++ pytest.raises(AssertionError, get_filter, lambda: None, 'test') + + + def test_callable_filter(): +@@ -474,41 +480,35 @@ class TestBuiltinFilters(TempEnvironment + self.mkbundle('foo.css', filters='cssmin', output='out.css').build() + except EnvironmentError: + # cssmin is not installed, that's ok. +- raise SkipTest() ++ pytest.skip('no cssmin') + assert self.get('out.css') == """h1{font-family:"Verdana";color:#FFF}""" + + def test_cssutils(self): +- try: +- import cssutils +- except ImportError: +- raise SkipTest() ++ cssutils = pytest.importorskip('cssutils') + self.mkbundle('foo.css', filters='cssutils', output='out.css').build() + assert self.get('out.css') == """h1{font-family:"Verdana";color:#FFF}""" + + def test_clevercss(self): +- try: +- import clevercss +- except ImportError: +- raise SkipTest() ++ clevercss = pytest.importorskip('clevercss') + self.create_files({'in': """a:\n color: #fff.darken(50%)"""}) + self.mkbundle('in', filters='clevercss', output='out.css').build() + assert self.get('out.css') == """a {\n color: #7f7f7f;\n}""" + + def test_uglifyjs_ascii(self): + if not find_executable('uglifyjs'): +- raise SkipTest() ++ pytest.skip('no uglifyjs') + self.mkbundle('foo2.js', filters='uglifyjs', output='out.js').build() + assert self.get('out.js') == 'more();' + + def test_uglifyjs_unicode(self): + if not find_executable('uglifyjs'): +- raise SkipTest() ++ pytest.skip('no uglifyjs') + self.mkbundle('foo.js', filters='uglifyjs', output='out.js').build() + assert self.get('out.js') == 'function foo(bar){var dummy;document.write(bar);var a="Ünícôdè"}' + + def test_uglifyjs_ascii_and_unicode(self): + if not find_executable('uglifyjs'): +- raise SkipTest() ++ pytest.skip('no uglifyjs') + self.mkbundle('foo.js', 'foo2.js', filters='uglifyjs', output='out.js').build() + assert self.get('out.js') == 'function foo(bar){var dummy;document.write(bar);var a="Ünícôdè"}more();' + +@@ -516,35 +516,32 @@ class TestBuiltinFilters(TempEnvironment + try: + self.mkbundle('foo2.js', filters='slimit', output='out.js').build() + except EnvironmentError: +- raise SkipTest("slimit is not installed") ++ pytest.skip("slimit is not installed") + assert self.get('out.js') == 'more();' + + def test_slimit_unicode(self): + try: + self.mkbundle('foo.js', filters='slimit', output='out.js').build() + except EnvironmentError: +- raise SkipTest("slimit is not installed") ++ pytest.skip("slimit is not installed") + assert self.get('out.js') == 'function foo(bar){var dummy;document.write(bar);var a="Ünícôdè";}' + + def test_slimit_ascii_and_unicode(self): + try: + self.mkbundle('foo.js', 'foo2.js', filters='slimit', output='out.js').build() + except EnvironmentError: +- raise SkipTest("slimit is not installed") ++ pytest.skip("slimit is not installed") + assert self.get('out.js') == 'function foo(bar){var dummy;document.write(bar);var a="Ünícôdè";}more();' + + def test_less_ruby(self): + # TODO: Currently no way to differentiate the ruby lessc from the + # JS one. Maybe the solution is just to remove the old ruby filter. +- raise SkipTest() ++ pytest.skip() + self.mkbundle('foo.css', filters='less_ruby', output='out.css').build() + assert self.get('out.css') == 'h1 {\n font-family: "Verdana";\n color: #ffffff;\n}\n' + + def test_jsmin(self): +- try: +- import jsmin +- except ImportError: +- raise SkipTest() ++ jsmin = pytest.importorskip('jsmin') + self.mkbundle('foo.js', filters='jsmin', output='out.js').build() + assert self.get('out.js') in ( + # Builtin jsmin +@@ -556,10 +553,7 @@ class TestBuiltinFilters(TempEnvironment + ) + + def test_rjsmin(self): +- try: +- import rjsmin +- except ImportError: +- raise SkipTest() ++ rjsmin = pytest.importorskip('rjsmin') + self.mkbundle('foo.js', filters='rjsmin', output='out.js').build() + assert self.get('out.js') == 'function foo(bar){var dummy;document.write(bar);var a="\xc3\x9cn\xc3\xadc\xc3\xb4d\xc3\xa8";}' + +@@ -568,38 +562,29 @@ class TestBuiltinFilters(TempEnvironment + assert self.get('out.js').startswith('eval(function(p,a,c,k,e,d)') + + def test_yui_js(self): +- try: +- import yuicompressor +- except ImportError: +- raise SkipTest() ++ yuicompressor = pytest.importorskip('yuicompressor') + self.mkbundle('foo.js', filters='yui_js', output='out.js').build() + assert self.get('out.js') == 'function foo(c){var d;document.write(c);var b="Ünícôdè"};' + + def test_yui_css(self): +- try: +- import yuicompressor +- except ImportError: +- raise SkipTest() ++ yuicompressor = pytest.importorskip('yuicompressor') + self.mkbundle('foo.css', filters='yui_css', output='out.css').build() + assert self.get('out.css') == """h1{font-family:"Verdana";color:#fff}""" + + def test_cleancss(self): + if not find_executable('cleancss'): +- raise SkipTest() ++ pytest.skip('no cleancss') + self.mkbundle('foo.css', filters='cleancss', output='out.css').build() + assert self.get('out.css') in ('h1{font-family:Verdana;color:#FFF}', 'h1{font-family:Verdana;color:#fff}') + + def test_cssslimmer(self): +- try: +- import slimmer +- except ImportError: +- raise SkipTest() ++ slimmer = pytest.importorskip('slimmer') + self.mkbundle('foo.css', filters='css_slimmer', output='out.css').build() + assert self.get('out.css') == 'h1{font-family:"Verdana";color:#FFF}' + + def test_stylus(self): + if not find_executable('stylus'): +- raise SkipTest() ++ pytest.skip('no stylus') + self.create_files({'in': """a\n width:100px\n height:(@width/2)"""}) + self.mkbundle('in', filters='stylus', output='out.css').build() + assert self.get('out.css') == """a {\n width: 100px;\n height: 50px;\n}\n\n""" +@@ -608,7 +593,7 @@ class TestBuiltinFilters(TempEnvironment + try: + self.mkbundle('foo.css', filters='rcssmin', output='out.css').build() + except EnvironmentError: +- raise SkipTest() ++ pytest.skip('no rcssmin') + assert self.get('out.css') == """h1{font-family:"Verdana";color:#FFFFFF}""" + + def test_find_pyc_files( self ): +@@ -625,10 +610,7 @@ class TestBuiltinFilters(TempEnvironment + class TestCSSPrefixer(TempEnvironmentHelper): + + def setup(self): +- try: +- import cssprefixer +- except ImportError: +- raise SkipTest() ++ cssprefixer = pytest.importorskip('cssprefixer') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -646,7 +628,7 @@ class TestCoffeeScript(TempEnvironmentHe + + def setup(self): + if not find_executable('coffee'): +- raise SkipTest() ++ pytest.skip('no coffee') + TempEnvironmentHelper.setup(self) + + def test_default_options(self): +@@ -669,10 +651,7 @@ class TestCoffeeScript(TempEnvironmentHe + class TestJinja2(TempEnvironmentHelper): + + def setup(self): +- try: +- import jinja2 +- except ImportError: +- raise SkipTest() ++ jinja2 = pytest.importorskip('jinja2') + TempEnvironmentHelper.setup(self) + + def test_default_options(self): +@@ -700,11 +679,7 @@ class TestClosure(TempEnvironmentHelper) + } + + def setup(self): +- try: +- import closure +- except ImportError: +- raise SkipTest() +- ++ closure = pytest.importorskip('closure') + TempEnvironmentHelper.setup(self) + + def test_closure(self): +@@ -828,7 +803,7 @@ class TestLess(TempEnvironmentHelper): + + def setup(self): + if not find_executable('lessc'): +- raise SkipTest() ++ pytest.skip('no lessc') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -904,7 +879,7 @@ class TestLess(TempEnvironmentHelper): + 'p1', 'p2', filters=less_output, output='out2.css' + ).build() + +- assert_raises(FilterError, mkbundle) ++ pytest.raises(FilterError, mkbundle) + + + class TestRubySass(TempEnvironmentHelper): +@@ -924,10 +899,10 @@ class TestRubySass(TempEnvironmentHelper + + def setup(self): + if not find_executable('sass'): +- raise SkipTest() ++ pytest.skip('no sass') + + if "Ruby" not in check_output(["sass", "--version"]).decode('utf-8'): +- raise SkipTest() ++ pytest.skip('no Ruby') + + TempEnvironmentHelper.setup(self) + +@@ -1034,7 +1009,7 @@ class TestSass(TempEnvironmentHelper): + + def setup(self): + if not find_executable('sass'): +- raise SkipTest() ++ pytest.skip('no sass') + TempEnvironmentHelper.setup(self) + + def test_sass(self): +@@ -1098,7 +1073,7 @@ class TestPyScss(TempEnvironmentHelper): + import scss + self.scss = scss + except ImportError: +- raise SkipTest() ++ pytest.skip('no scss') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -1113,7 +1088,7 @@ class TestPyScss(TempEnvironmentHelper): + from PIL import Image + Image.new('RGB', (10,10)).save(StringIO(), 'png') + except (ImportError, IOError): +- raise SkipTest() ++ pytest.skip('no PIL or Pillow') + self.create_files({'noise.scss': 'h1 {background: background-noise()}'}) + self.mkbundle('noise.scss', filters='pyscss', output='out.css').build() + +@@ -1135,7 +1110,7 @@ class TestLibSass(TempEnvironmentHelper) + import sass + self.sass = sass + except ImportError: +- raise SkipTest() ++ pytest.skip('no sass') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -1194,7 +1169,7 @@ class TestCompass(TempEnvironmentHelper) + + def setup(self): + if not find_executable('compass'): +- raise SkipTest() ++ pytest.skip('no compass') + TempEnvironmentHelper.setup(self) + + def test_compass(self): +@@ -1420,7 +1395,7 @@ class TestHandlebars(TempEnvironmentHelp + + def setup(self): + if not find_executable('handlebars'): +- raise SkipTest() ++ pytest.skip('no handlebars') + TempEnvironmentHelper.setup(self) + + def test_basic(self): +@@ -1456,10 +1431,7 @@ class TestJinja2JS(TempEnvironmentHelper + } + + def setup(self): +- try: +- import closure_soy +- except: +- raise SkipTest() ++ closure_soy = pytest.importorskip('closure_soy') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -1486,7 +1458,7 @@ class TestTypeScript(TempEnvironmentHelp + + def setup(self): + if not find_executable('tsc'): +- raise SkipTest() ++ pytest.skip('no tsc') + TempEnvironmentHelper.setup(self) + + def test(self): +@@ -1517,7 +1489,7 @@ define("script/app",["./utils"],function + + def setup(self): + if not find_executable('r.js'): +- raise SkipTest('"r.js" executable not found') ++ pytest.skip('"r.js" executable not found') + TempEnvironmentHelper.setup(self) + self.env.config['requirejs_config'] = self.path('requirejs.json') + self.env.config['requirejs_baseUrl'] = self.path('') +@@ -1569,7 +1541,7 @@ class TestClosureStylesheets(TempEnviron + + def setup(self): + if not 'CLOSURE_STYLESHEETS_PATH' in os.environ: +- raise SkipTest() ++ pytest.skip('no CLOSURE_STYLESHEETS_PATH in env') + TempEnvironmentHelper.setup(self) + + def test_compiler(self): +@@ -1596,7 +1568,7 @@ class TestAutoprefixer6Filter(TempEnviro + except FilterError as e: + # postcss is not installed, that's ok. + if 'Program file not found' in e.message: +- raise SkipTest() ++ pytest.skip(e.message) + else: + raise + out = self.get('output.css') +@@ -1615,7 +1587,7 @@ class TestBabel(TempEnvironmentHelper): + except FilterError as e: + # babel is not installed, that's ok. + if 'Program file not found' in str(e): +- raise SkipTest() ++ pytest.skip(e.message) + else: + raise + assert "var x = function x" in self.get('output.js') +@@ -1627,7 +1599,7 @@ class TestBabel(TempEnvironmentHelper): + except FilterError as e: + # babel is not installed, that's ok. + if 'Program file not found' in e.message: +- raise SkipTest() ++ pytest.skip(e.message) + else: + raise + assert (self.get('output.js').strip() == +Index: webassets-2.0/tests/test_loaders.py +=================================================================== +--- webassets-2.0.orig/tests/test_loaders.py ++++ webassets-2.0/tests/test_loaders.py +@@ -1,6 +1,8 @@ + from __future__ import with_statement + import sys +-from nose.tools import assert_raises, assert_true ++ ++import pytest ++ + import textwrap + from webassets.env import Environment + from webassets.filter import Filter, get_filter +@@ -8,16 +10,12 @@ from webassets.utils import StringIO + from webassets.bundle import Bundle + from webassets.loaders import PythonLoader, YAMLLoader, LoaderError + from webassets.exceptions import EnvironmentError +-from nose import SkipTest + + + class TestYAML(object): + + def setup(self): +- try: +- import yaml +- except ImportError: +- raise SkipTest() ++ yaml = pytest.importorskip("yaml") + + def loader(self, text, filename=None): + io = StringIO(textwrap.dedent(text)) +@@ -195,10 +193,7 @@ class TestYAMLCustomFilters(TestYAML): + super(TestYAMLCustomFilters, self).setup() + + # If zope.dottedname is not installed, that's OK +- try: +- import zope.dottedname.resolve +- except ImportError: +- raise SkipTest() ++ pytest.importorskip("zope.dottedname.resolve") + # Save off the original get_import_resolver + self.original_resolver = YAMLLoader._get_import_resolver + # Make a mock +@@ -231,7 +226,7 @@ class TestYAMLCustomFilters(TestYAML): + filters: + - webassets.filter.less.Less + """) +- assert_raises(EnvironmentError, loader.load_environment) ++ pytest.raises(EnvironmentError, loader.load_environment) + self.reset_importer() + + def test_load_filter_module_throws_exc(self): +@@ -241,7 +236,7 @@ class TestYAMLCustomFilters(TestYAML): + filters: + - webassets.filter.less + """) +- assert_raises(LoaderError, loader.load_environment) ++ pytest.raises(LoaderError, loader.load_environment) + + def test_bad_filter_throws_exc(self): + """ Test that importing filters that don't exist throws an exception """ +@@ -249,7 +244,7 @@ class TestYAMLCustomFilters(TestYAML): + filters: + - webassets.fake.filter + """) +- assert_raises(LoaderError, loader.load_environment) ++ pytest.raises(LoaderError, loader.load_environment) + + def test_load_filters(self): + """Check that filters can be loaded from YAML """ +@@ -257,11 +252,11 @@ class TestYAMLCustomFilters(TestYAML): + import webassets.filter + del webassets.filter._FILTERS['less'] + # Verify that it was deleted +- assert_raises(ValueError, get_filter, 'less') ++ pytest.raises(ValueError, get_filter, 'less') + # Load it again from YAML + self.loader(""" + filters: + - webassets.filter.less.Less + """).load_environment() + # Check that it's back +- assert_true(isinstance(get_filter('less'), Filter)) ++ assert isinstance(get_filter('less'), Filter) +Index: webassets-2.0/tests/test_script.py +=================================================================== +--- webassets-2.0.orig/tests/test_script.py ++++ webassets-2.0/tests/test_script.py +@@ -8,14 +8,12 @@ from __future__ import with_statement + + import logging + from threading import Thread, Event +-from nose.tools import assert_raises +-from nose import SkipTest ++ ++import pytest ++ + import time + +-try: +- import argparse +-except ImportError: +- raise SkipTest() ++argparse = pytest.importorskip("argparse") + + from webassets import Bundle + from webassets.exceptions import BuildError +@@ -96,7 +94,7 @@ class TestBuildCommand(TestCLI): + + # Building to a non-existing path would fail, directories + # are not auto-created here. +- assert_raises(IOError, self.cmd_env.build, ++ pytest.raises(IOError, self.cmd_env.build, + output=[('a', self.path('new/custom'))]) + + def test_custom_directory(self): +@@ -119,7 +117,7 @@ class TestBuildCommand(TestCLI): + b2 = MockBundle(output='b2') + b = MockBundle(b1, b2) + self.assets_env.add(b) +- assert_raises(CommandError, self.cmd_env.build, ++ pytest.raises(CommandError, self.cmd_env.build, + directory=self.path('some/path')) + + def test_no_cache(self): +@@ -313,15 +311,12 @@ class TestArgparseImpl(TestWatchMixin, T + the commandline, we fail with a clean error. + """ + impl = GenericArgparseImplementation(env=None) +- assert_raises(CommandError, impl.run_with_argv, ['build']) ++ pytest.raises(CommandError, impl.run_with_argv, ['build']) + + def test_watch_config_file(self): + """The watch command has an eye on the config file. This is an + extension to the base watch command.""" +- try: +- import yaml +- except ImportError: +- raise SkipTest() ++ yaml = pytest.importorskip("yaml") + + self.cmd_env = CommandLineEnvironment(self.env, logging) + self.cmd_env.commands['watch'] = \ +Index: webassets-2.0/tests/test_updaters.py +=================================================================== +--- webassets-2.0.orig/tests/test_updaters.py ++++ webassets-2.0/tests/test_updaters.py +@@ -1,5 +1,7 @@ + import os +-from nose.tools import assert_raises ++ ++import pytest ++ + from webassets import Environment, Bundle + from webassets.exceptions import BundleError, BuildError + from webassets.updater import TimestampUpdater, BundleDefUpdater, SKIP_CACHE +@@ -201,7 +203,7 @@ class TestTimestampUpdater(TempEnvironme + an error is raised. + """ + bundle = self.mkbundle('in', output='out', depends=('file',)) +- assert_raises(BundleError, self.updater.needs_rebuild, bundle, self.env) ++ pytest.raises(BundleError, self.updater.needs_rebuild, bundle, self.env) + + def test_changed_file_after_nested_bundle(self): + """[Regression] Regression-test for a particular bug where the +@@ -264,7 +266,7 @@ class TestTimestampUpdater(TempEnvironme + b = self.mkbundle('in', output='out-%(version)s') + + # Confirm DummyVersion works as we expect it to. +- assert_raises(VersionIndeterminableError, ++ pytest.raises(VersionIndeterminableError, + self.env.versions.determine_version, b, self.env) + + # Without a manifest, an error is raised. With no version being +@@ -272,7 +274,7 @@ class TestTimestampUpdater(TempEnvironme + # We would have to blindly return YES, PROCEED WITH BUILD every + # time, thus not doing our job. + self.env.manifest = None +- assert_raises(BuildError, self.env.updater.needs_rebuild, b, self.env) ++ pytest.raises(BuildError, self.env.updater.needs_rebuild, b, self.env) + + # As soon as a manifest is set, the updater will start to work, + # even if the manifest does not actually have a version. This is +Index: webassets-2.0/tests/test_version.py +=================================================================== +--- webassets-2.0.orig/tests/test_version.py ++++ webassets-2.0/tests/test_version.py +@@ -3,7 +3,8 @@ + import hashlib + + import os +-from nose.tools import assert_raises ++ ++import pytest + + from webassets.env import Environment + from webassets.merge import MemoryHunk +@@ -60,7 +61,7 @@ class TestTimestampVersion(TempEnvironme + + # What if the output file does not exist? (this should not happen, right?) + self.unlink('out') +- assert_raises(OSError, self.v.determine_version, ++ pytest.raises(OSError, self.v.determine_version, + self.bundle, self.env, None) + + def test_with_placeholder(self): +@@ -71,7 +72,7 @@ class TestTimestampVersion(TempEnvironme + + # If any source file is missing, the updater cannot do its job. + self.unlink('dep') +- assert_raises(VersionIndeterminableError, self.v.determine_version, ++ pytest.raises(VersionIndeterminableError, self.v.determine_version, + self.bundle, self.env, None) + + def test_outputfile_timestamp(self): +@@ -127,13 +128,13 @@ class TestHashVersion(TempEnvironmentHel + + # What if the output file does not exist? (this should not happen, right?) + self.unlink('out') +- assert_raises(IOError, self.v.determine_version, ++ pytest.raises(IOError, self.v.determine_version, + self.bundle, self.env, None) + + def test_with_placeholder(self): + # The HashVersion cannot function in this case. + self.bundle.output = 'out-%(version)s' +- assert_raises(VersionIndeterminableError, self.v.determine_version, ++ pytest.raises(VersionIndeterminableError, self.v.determine_version, + self.bundle, self.env, None) + + +@@ -221,7 +222,7 @@ class TestCacheManifest(TempEnvironmentH + + # If no cache is enabled, an error is raised + self.env.cache = False +- assert_raises(EnvironmentError, ++ pytest.raises(EnvironmentError, + manifest.remember, self.bundle, self.env, 'the-version') +- assert_raises(EnvironmentError, ++ pytest.raises(EnvironmentError, + manifest.query, self.bundle, self.env) diff --git a/webassets-py39-threading.patch b/webassets-py39-threading.patch new file mode 100644 index 0000000..e507aaa --- /dev/null +++ b/webassets-py39-threading.patch @@ -0,0 +1,22 @@ +From a563935df6702ff5e38e5b84a262c295e4cdf455 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Singaravelan +Date: Wed, 22 Jan 2020 00:49:31 +0530 +Subject: [PATCH] Use is_alive instead of isAlive for Python 3.9 compatibility. + +--- + tests/test_script.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test_script.py b/tests/test_script.py +index a83bc07f..d17a83bb 100644 +--- a/tests/test_script.py ++++ b/tests/test_script.py +@@ -192,7 +192,7 @@ def start_watching(self): + + def stop_watching(self): + """Stop the watch command thread.""" +- assert self.t.isAlive() # If it has already ended, something is wrong ++ assert self.t.is_alive() # If it has already ended, something is wrong + self.stopped = True + self.t.join(1) +