I am having an issue where a Flask app using flask_assets
can't find lessc
after a modify a certain .less
file, and even changing it back to the original does not help.
I have been trying to use this tutorial to learn about using Blueprints to organize a Flask application: https://hackersandslackers.com/flask-blueprints/
app structure
├── application
│ ├── assets.py
│ ├── home
│ │ ├── home.py
│ │ ├── __pycache__
│ │ │ └── home.cpython-38.pyc
│ │ ├── static
│ │ │ └── less
│ │ │ ├── home.less
│ │ │ └── variables.less
│ │ └── templates
│ │ └── index.jinja2
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── assets.cpython-38.pyc
│ │ └── __init__.cpython-38.pyc
│ ├── static
│ │ ├── dist
│ │ │ └── css
│ │ │ ├── home.css
│ │ │ ├── products.css
│ │ │ ├── profile.css
│ │ │ └── style.css
│ │ ├── img
│ │ │ ├── avatar.png
│ │ │ ├── favicon.png
│ │ │ └── logo.png
│ │ └── src
│ │ └── less
│ │ ├── nav.less
│ │ ├── style.less
│ │ └── variables.less
│ └── templates
│ ├── analytics.jinja2
│ ├── layout.jinja2
│ └── navigation.jinja2
└── wsgi.py
init.py
"""Initialize Flask app."""
from flask import Flask
from flask_assets import Environment
def create_app():
"""Create Flask application."""
app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
assets = Environment()
assets.init_app(app)
with app.app_context():
# Import parts of our application
from .home import home
from .assets import compile_static_assets
# Register Blueprints
app.register_blueprint(home.home_bp)
# Compile static assets
compile_static_assets(assets)
return app
```
assets.py
```
from flask import current_app as app
from flask_assets import Bundle
def compile_static_assets(assets):
assets.auto_build = True
assets.debug = False
common_less_bundle = Bundle('src/less/*.less',
filters='less,cssmin',
output='dist/css/style.css',
extra={'rel': 'stylesheet/less'})
home_less_bundle = Bundle('home_bp/less/home.less',
filters='less,cssmin',
output='dist/css/home.css',
extra={'rel': 'stylesheet/less'})
assets.register('common_less_bundle', common_less_bundle)
assets.register('home_less_bundle', home_less_bundle)
if app.config['ENV'] == 'development': # Only rebuild bundles in development
common_less_bundle.build()
home_less_bundle.build()
return assets
home.py
"""General page routes."""
from flask import Blueprint, render_template
from flask import current_app as app
# Blueprint Configuration
home_bp = Blueprint('home_bp', __name__,
template_folder='templates',
static_folder='static')
@home_bp.route('/', methods=['GET'])
def home():
"""Homepage."""
products ="example"
return render_template('index.jinja2',
title='Flask Blueprint Demo',
subtitle='Demonstration of Flask blueprints in action.',
template='home-template',
products=products)
and the problem file, home.less
@import 'variables.less';
.home-template {
&.page {
.products {
display: none !important;
}
}
.container {
.resource-links {
margin-top: 10px;
.resource-link {
margin: 4px 0;
color: #8a91a7;
line-height: 1;
}
}
.products {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-top: 30px;
.product-preview {
padding: 2%;
box-shadow: 0 0 5px rgba(65, 67, 144, 0.15);
max-width: 28%;
width: 30%;
background: white;
margin-bottom: 20px;
display: block;
text-align: center;
transition: @transition;
&:hover {
background: #5dbad7;
color: white !important;
opacity: 1;
* {
color: white !important;
}
}
.product-image {
height: 110px;
margin: 0 auto 20px;
display: block;
@media (max-width: 600px) {
width: 90%;
height: unset;
}
}
.name {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
width: -webkit-fill-available;
width: -moz-available;
width: stretch;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
font-size: .9em;
height: 42px;
}
.price {
color: #5f6988;
margin-top: 5px;
font-size: 1.1em;
font-weight: 600;
}
}
}
}
h2 {
margin-bottom: 0 !important;
}
}
When home.less
is freshly pasted from the git download to my simplified version, the app runs as expected.
$ python3 wsgi.py
* Serving Flask app "application" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5001/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 213-702-582
127.0.0.1 - - [21/May/2020 14:26:01] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [21/May/2020 14:26:02] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [21/May/2020 14:26:02] "GET /static/dist/css/home.css?99706b81 HTTP/1.1" 304 -
127.0.0.1 - - [21/May/2020 14:26:02] "GET /static/img/logo.png HTTP/1.1" 304 -
127.0.0.1 - - [21/May/2020 14:26:02] "GET /favicon.ico HTTP/1.1" 404 -
Now, what really wierds me out, is that if I make any modification to home.less
, even just adding a space, saving, removing the space, and then saving again, the less
filter cannot be found:
* Serving Flask app "application" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5001/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 213-702-582
127.0.0.1 - - [21/May/2020 14:28:09] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/__init__.py", line 510, in subprocess
proc = subprocess.Popen(
File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'lessc'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/home.py", line 15, in home
return render_template('index.jinja2',
File "/usr/lib/python3.8/site-packages/flask/templating.py", line 137, in render_template
return _render(
File "/usr/lib/python3.8/site-packages/flask/templating.py", line 120, in _render
rv = template.render(context)
File "/usr/lib/python3.8/site-packages/jinja2/environment.py", line 1090, in render
self.environment.handle_exception()
File "/usr/lib/python3.8/site-packages/jinja2/environment.py", line 832, in handle_exception
reraise(*rewrite_traceback_stack(source=source))
File "/usr/lib/python3.8/site-packages/jinja2/_compat.py", line 28, in reraise
raise value.with_traceback(tb)
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/templates/index.jinja2", line 1, in top-level template code
{% extends "layout.jinja2" %}
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/templates/layout.jinja2", line 9, in top-level template code
{% block pagestyles %}{% endblock %}
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/templates/index.jinja2", line 4, in block "pagestyles"
{% assets "home_less_bundle" %}
File "/home/james/.local/lib/python3.8/site-packages/webassets/ext/jinja2.py", line 187, in _render_assets
urls = bundle.urls(calculate_sri=True)
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 833, in urls
urls.extend(bundle._urls(new_ctx, extra_filters, *args, **kwargs))
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 767, in _urls
self._build(ctx, extra_filters=extra_filters, force=False,
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 618, in _build
hunk = self._merge_and_apply(
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 543, in _merge_and_apply
hunk = filtertool.apply(hunk, filters_to_run, 'input',
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 280, in apply
return self._wrap_cache(key, func)
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 222, in _wrap_cache
content = func().getvalue()
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 255, in func
getattr(filter, type)(data, out, **kwargs_final)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/less.py", line 139, in input
self._apply_less(_in, out, source_path)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/less.py", line 131, in _apply_less
self.subprocess(args, out, in_)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/__init__.py", line 520, in subprocess
raise FilterError('Program file not found: %s.' % argv[0])
webassets.exceptions.FilterError: Program file not found: lessc.
127.0.0.1 - - [21/May/2020 14:28:10] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/__init__.py", line 510, in subprocess
proc = subprocess.Popen(
File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'lessc'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/home.py", line 15, in home
return render_template('index.jinja2',
File "/usr/lib/python3.8/site-packages/flask/templating.py", line 137, in render_template
return _render(
File "/usr/lib/python3.8/site-packages/flask/templating.py", line 120, in _render
rv = template.render(context)
File "/usr/lib/python3.8/site-packages/jinja2/environment.py", line 1090, in render
self.environment.handle_exception()
File "/usr/lib/python3.8/site-packages/jinja2/environment.py", line 832, in handle_exception
reraise(*rewrite_traceback_stack(source=source))
File "/usr/lib/python3.8/site-packages/jinja2/_compat.py", line 28, in reraise
raise value.with_traceback(tb)
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/templates/index.jinja2", line 1, in top-level template code
{% extends "layout.jinja2" %}
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/templates/layout.jinja2", line 9, in top-level template code
{% block pagestyles %}{% endblock %}
File "/home/james/Downloads/flask-blueprint-tutorial-master_reduced/application/home/templates/index.jinja2", line 4, in block "pagestyles"
{% assets "home_less_bundle" %}
File "/home/james/.local/lib/python3.8/site-packages/webassets/ext/jinja2.py", line 187, in _render_assets
urls = bundle.urls(calculate_sri=True)
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 833, in urls
urls.extend(bundle._urls(new_ctx, extra_filters, *args, **kwargs))
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 767, in _urls
self._build(ctx, extra_filters=extra_filters, force=False,
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 618, in _build
hunk = self._merge_and_apply(
File "/home/james/.local/lib/python3.8/site-packages/webassets/bundle.py", line 543, in _merge_and_apply
hunk = filtertool.apply(hunk, filters_to_run, 'input',
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 280, in apply
return self._wrap_cache(key, func)
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 222, in _wrap_cache
content = func().getvalue()
File "/home/james/.local/lib/python3.8/site-packages/webassets/merge.py", line 255, in func
getattr(filter, type)(data, out, **kwargs_final)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/less.py", line 139, in input
self._apply_less(_in, out, source_path)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/less.py", line 131, in _apply_less
self.subprocess(args, out, in_)
File "/home/james/.local/lib/python3.8/site-packages/webassets/filter/__init__.py", line 520, in subprocess
raise FilterError('Program file not found: %s.' % argv[0])
webassets.exceptions.FilterError: Program file not found: lessc.
What could be going on here?
At first I thought it might be a cache issue, but deleting the __pycache__
did not help.
OK, well, I found a solution, but I really don't understand why it works.
If I change
assets.py
to explicitly create the environment, trivial modifications tohome.less
no longer result in the inability to find lessc.New assets.py
It makes sense to me that instantiating the environment with the right packages loaded is necessary for the
less
filter to work. But I am still mystified by the fact that this only became a problem after adding (and then removing) a single space fromless.css
.I think I must not understand the pipeline of how flask_assets applies filters. I've taken a gander at the docs, but it is not obvious to me.