diff --git a/.gitignore b/.gitignore
index eb9ee8c..ebcd75c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@
/build/
/dist/
/src/Flask_Assets.egg-info/
+venv
+tests/static/out
+tests/static/.webassets-cache
\ No newline at end of file
diff --git a/.idea/flask-assets.iml b/.idea/flask-assets.iml
index 31840df..11ff9e0 100644
--- a/.idea/flask-assets.iml
+++ b/.idea/flask-assets.iml
@@ -6,9 +6,9 @@
+
-
-
+
\ No newline at end of file
diff --git a/requirements-dev.pip b/requirements-dev.pip
index 75a296f..72e54fb 100644
--- a/requirements-dev.pip
+++ b/requirements-dev.pip
@@ -1,5 +1,6 @@
-nose
+packaging
Flask-Script>=0.3.3
-webassets==0.11.1
+webassets~=2.0
PyYAML
-pyScss>=1.1.5
+pyScss>=1.4.0
+pytest
diff --git a/src/flask_assets.py b/src/flask_assets.py
index 957ab9d..0e8c0a1 100644
--- a/src/flask_assets.py
+++ b/src/flask_assets.py
@@ -5,7 +5,9 @@
import logging
from os import path
-from flask import _request_ctx_stack, current_app
+from packaging import version
+
+import flask
from flask.templating import render_template_string
# We want to expose Bundle via this module.
from webassets import Bundle
@@ -270,9 +272,22 @@ def convert_item_to_flask_url(self, ctx, item, filepath=None):
filename = rel_path
flask_ctx = None
- if not _request_ctx_stack.top:
+ context_missing = False
+ if version.parse(flask.__version__) < version.parse("2.2.0"):
+ if not flask._request_ctx_stack.top:
+ context_missing = True
+ else:
+ try:
+ flask.globals.request_ctx.request
+ except RuntimeError as rte:
+ if not str(rte).startswith('Working outside of request context'):
+ raise
+ context_missing = True
+
+ if context_missing:
flask_ctx = ctx.environment._app.test_request_context()
flask_ctx.push()
+
try:
url = url_for(endpoint, filename=filename)
# In some cases, url will be an absolute url with a scheme and hostname.
@@ -314,13 +329,20 @@ def _app(self):
if self.app is not None:
return self.app
- ctx = _request_ctx_stack.top
+ if version.parse(flask.__version__) < version.parse("2.2.0"):
+ ctx = flask._request_ctx_stack.top
+ else:
+ ctx = flask.globals.request_ctx
+
if ctx is not None:
return ctx.app
try:
- from flask import _app_ctx_stack
- app_ctx = _app_ctx_stack.top
+ if version.parse(flask.__version__) < version.parse("2.2.0"):
+ from flask import _app_ctx_stack
+ app_ctx = _app_ctx_stack.top
+ else:
+ app_ctx = flask.globals.app_ctx
if app_ctx is not None:
return app_ctx.app
except ImportError:
@@ -329,8 +351,6 @@ def _app(self):
raise RuntimeError('assets instance not bound to an application, '+
'and no application in current context')
-
-
# XXX: This is required because in a couple of places, webassets 0.6
# still access env.directory, at one point even directly. We need to
# fix this for 0.6 compatibility, but it might be preferable to
@@ -338,6 +358,7 @@ def _app(self):
# like the cache directory and output files.
def set_directory(self, directory):
self.config['directory'] = directory
+
def get_directory(self):
if self.config.get('directory') is not None:
return self.config['directory']
@@ -475,7 +496,7 @@ def _webassets_cmd(cmd):
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
cmdenv = CommandLineEnvironment(
- current_app.jinja_env.assets_environment, logger
+ flask.current_app.jinja_env.assets_environment, logger
)
getattr(cmdenv, cmd)()
diff --git a/tests/test_config.py b/tests/test_config.py
index fad37b5..6121a20 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -2,12 +2,11 @@
"""
from __future__ import absolute_import
-from tests.helpers import check_warnings
-from nose.tools import assert_raises
+import pytest
+
from flask import Flask
from flask_assets import Environment
-from webassets.exceptions import ImminentDeprecationWarning
try:
from webassets.updater import BaseUpdater
@@ -24,7 +23,7 @@ class TestConfigAppBound:
"""The extension is bound to a specific app.
"""
- def setup(self):
+ def setup_method(self):
self.app = Flask(__name__)
self.env = Environment(self.app)
@@ -68,13 +67,14 @@ class TestConfigNoAppBound:
"""The application is not bound to a specific app.
"""
- def setup(self):
+ def setup_method(self):
self.env = Environment()
def test_no_app_available(self):
"""Without an application bound, we can't do much."""
- assert_raises(RuntimeError, setattr, self.env, 'debug', True)
- assert_raises(RuntimeError, self.env.config.get, 'debug')
+ with pytest.raises(RuntimeError):
+ setattr(self.env, 'debug', True)
+ self.env.config.get('debug')
def test_global_defaults(self):
"""We may set defaults even without an application, however."""
@@ -89,7 +89,8 @@ def test_multiple_separate_apps(self):
self.env.init_app(app1)
# With no app yet available...
- assert_raises(RuntimeError, getattr, self.env, 'url')
+ with pytest.raises(RuntimeError):
+ getattr(self.env, 'url')
# ...set a default
self.env.config.setdefault('FOO', 'BAR')
@@ -109,6 +110,7 @@ def test_key_error(self):
"""KeyError is raised if a config value doesn't exist.
"""
with Flask(__name__).test_request_context():
- assert_raises(KeyError, self.env.config.__getitem__, 'YADDAYADDA')
+ with pytest.raises(KeyError):
+ self.env.config.__getitem__('YADDAYADDA')
# The get() helper, on the other hand, simply returns None
- assert self.env.config.get('YADDAYADDA') == None
+ assert self.env.config.get('YADDAYADDA') is None
diff --git a/tests/test_env.py b/tests/test_env.py
index 066f703..d428934 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -1,12 +1,11 @@
import os
-from nose.tools import assert_raises
from flask import Flask
from flask_assets import Environment, Bundle
class TestEnv:
- def setup(self):
+ def setup_method(self):
self.app = Flask(__name__)
self.env = Environment(self.app)
self.env.debug = True
diff --git a/tests/test_integration.py b/tests/test_integration.py
index d995e75..6b27423 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -1,11 +1,14 @@
from __future__ import absolute_import
-from nose.tools import assert_raises
+
+import flask
+import pytest
from flask import Flask
from flask_assets import Environment, Bundle
from webassets.bundle import get_all_bundle_files
from tests.helpers import TempEnvironmentHelper, Module, Blueprint
+from packaging import version
def test_import():
# We want to expose these via the assets extension module.
@@ -25,10 +28,17 @@ class TestUrlAndDirectory(TempEnvironmentHelper):
Let's test the different scenarios to ensure everything works.
"""
- def setup(self):
+ def setup_method(self):
TempEnvironmentHelper.setup(self)
- self.app = Flask(__name__, static_path='/app_static')
+ kwargs = {}
+ if int(flask.__version__.split('.')[0]) < 1:
+ kwargs['static_path'] = '/app_static'
+ else:
+ kwargs['static_url_path'] = '/app_static'
+
+ self.app = Flask(__name__, **kwargs)
+
from tests import test_module
if not Blueprint:
self.module = Module(test_module.__name__, name='module',
@@ -42,10 +52,11 @@ def setup(self):
self.env = Environment(self.app)
def test_config_values_not_set_by_default(self):
- assert not 'directory' in self.env.config
- assert not 'url' in self.env.config
- assert_raises(KeyError, self.env.config.__getitem__, 'directory')
- assert_raises(KeyError, self.env.config.__getitem__, 'url')
+ assert 'directory' not in self.env.config
+ assert 'url' not in self.env.config
+ with pytest.raises(KeyError):
+ self.env.config.__getitem__('directory')
+ self.env.config.__getitem__('url')
def test_directory_auto(self):
"""Test how we resolve file references through the Flask static
@@ -65,15 +76,15 @@ def test_directory_auto(self):
assert get_all_bundle_files(Bundle('./module/bar'), self.env) == [root + '/static/module/bar']
# Custom static folder
- self.app.static_folder = '/'
- assert get_all_bundle_files(Bundle('foo'), self.env) == ['/foo']
+ self.app.static_folder = '/bar'
+ assert get_all_bundle_files(Bundle('foo'), self.env)[0].endswith('/bar/foo')
def test_url_auto(self):
"""Test how urls are generated via the Flask static system
by default (if no custom 'env.url' etc. values have been
configured manually).
"""
- assert not 'url' in self.env.config
+ assert 'url' not in self.env.config
assert Bundle('foo', env=self.env).urls() == ['/app_static/foo']
# Urls for files that point to a module use that module's url prefix.
@@ -83,8 +94,14 @@ def test_url_auto(self):
# [Regression] Ensure that any request context we may have added
# to the stack has been removed.
- from flask import _request_ctx_stack
- assert _request_ctx_stack.top is None
+
+ if version.parse(flask.__version__) < version.parse("2.2.0"):
+ assert not flask._request_ctx_stack.top
+ else:
+ from flask.globals import request_ctx
+
+ with pytest.raises(RuntimeError):
+ assert not request_ctx.request
def test_custom_load_path(self):
"""A custom load_path is configured - this will affect how
@@ -96,7 +113,6 @@ def test_custom_load_path(self):
# We do not recognize references to modules.
assert get_all_bundle_files(Bundle('module/bar'), self.env) == [self.path('module/bar')]
-
assert Bundle('foo', env=self.env).urls() == ['/custom/foo']
assert Bundle('module/bar', env=self.env).urls() == ['/custom/module/bar']
@@ -149,8 +165,8 @@ class TestUrlAndDirectoryWithInitApp(object):
values also work if the application is initialized via "init_app()".
"""
- def setup(self):
- self.app = Flask(__name__, static_path='/initapp_static')
+ def setup_method(self):
+ self.app = Flask(__name__, static_url_path='/initapp_static')
self.env = Environment()
self.env.init_app(self.app)
@@ -233,7 +249,8 @@ def test_blueprint_urls(self):
def test_blueprint_no_static_folder(self):
"""Test dealing with a blueprint without a static folder."""
self.make_blueprint('module')
- assert_raises(TypeError, self.mkbundle('module/foo').urls)
+ with pytest.raises(TypeError):
+ self.mkbundle('module/foo').urls()
def test_cssrewrite(self):
"""Make sure cssrewrite works with Blueprints.
diff --git a/tests/test_script.py b/tests/test_script.py
index 38660ff..de8e237 100644
--- a/tests/test_script.py
+++ b/tests/test_script.py
@@ -1,22 +1,21 @@
from __future__ import absolute_import
-from nose import SkipTest
+import pytest
# check for flask-script before importing things that fail if it's not present
try:
from flask_script import Manager
except:
- raise SkipTest()
+ pytest.skip(allow_module_level=True)
import sys
-from flask import Flask
-from flask_assets import Environment, ManageAssets
+from flask_assets import ManageAssets
from webassets.script import GenericArgparseImplementation
from tests.helpers import TempEnvironmentHelper
# Flask-script seemingly no longer supports 2.6
if sys.version_info[:2] == (2, 6):
- raise SkipTest()
+ pytest.skip(allow_module_level=True)
# The CLI likes to log to stderr, which isn't nice to the test output.
diff --git a/tox.ini b/tox.ini
index 1d385d5..14263a3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,9 @@
[tox]
-envlist = py26, py27, py33, pypy
+envlist = py39, py310, pypy
[testenv]
-commands = nosetests tests
-deps =
- -r{toxinidir}/requirements-dev.pip
+commands =
+ pytest -W ignore::DeprecationWarning
+deps =
+ pytest
+ -r {toxinidir}/requirements-dev.pip