Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
/build/
/dist/
/src/Flask_Assets.egg-info/
venv
tests/static/out
tests/static/.webassets-cache
4 changes: 2 additions & 2 deletions .idea/flask-assets.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions requirements-dev.pip
Original file line number Diff line number Diff line change
@@ -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
37 changes: 29 additions & 8 deletions src/flask_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand All @@ -329,15 +351,14 @@ 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
# introduce another API similar to _normalize_source_path() for things
# 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']
Expand Down Expand Up @@ -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)()

Expand Down
22 changes: 12 additions & 10 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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."""
Expand All @@ -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')

Expand All @@ -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
3 changes: 1 addition & 2 deletions tests/test_env.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
49 changes: 33 additions & 16 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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',
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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']

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand Down
9 changes: 4 additions & 5 deletions tests/test_script.py
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
10 changes: 6 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -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