Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-pipeline
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
django-pipeline
Commits
452f97f9
Commit
452f97f9
authored
Mar 07, 2016
by
David Trowbridge
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #545 from chipx86/inline-compile-errors
Add error output for compiler errors within the browser.
parents
f2366b5e
3b5c5a22
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
108 additions
and
13 deletions
+108
-13
docs/configuration.rst
+12
-0
pipeline/compilers/__init__.py
+6
-3
pipeline/conf.py
+2
-0
pipeline/exceptions.py
+5
-1
pipeline/jinja2/__init__.py
+2
-2
pipeline/templates/pipeline/compile_error.html
+31
-0
pipeline/templatetags/pipeline.py
+34
-5
tests/tests/test_compiler.py
+16
-2
No files found.
docs/configuration.rst
View file @
452f97f9
...
@@ -145,6 +145,18 @@ Defaults to ``True``
...
@@ -145,6 +145,18 @@ Defaults to ``True``
this only work when PIPELINE_ENABLED is False.
this only work when PIPELINE_ENABLED is False.
``SHOW_ERRORS_INLINE``
......................
``True`` if errors compiling CSS/JavaScript files should be shown inline at
the top of the browser window, or ``False`` if they should trigger exceptions
(the older behavior).
This only applies when compiling through the ``{% stylesheet %}`` or
``{% javascript %}`` template tags. It won't impact ``collectstatic``.
Defaults to ``settings.DEBUG``.
``CSS_COMPRESSOR``
``CSS_COMPRESSOR``
..................
..................
...
...
pipeline/compilers/__init__.py
View file @
452f97f9
...
@@ -8,7 +8,7 @@ from django.contrib.staticfiles import finders
...
@@ -8,7 +8,7 @@ from django.contrib.staticfiles import finders
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
django.core.files.base
import
ContentFile
from
django.core.files.base
import
ContentFile
from
django.utils.encoding
import
smart_bytes
from
django.utils.encoding
import
smart_bytes
from
django.utils.six
import
string_types
from
django.utils.six
import
string_types
,
text_type
from
pipeline.conf
import
settings
from
pipeline.conf
import
settings
from
pipeline.exceptions
import
CompilerError
from
pipeline.exceptions
import
CompilerError
...
@@ -125,7 +125,9 @@ class SubProcessCompiler(CompilerBase):
...
@@ -125,7 +125,9 @@ class SubProcessCompiler(CompilerBase):
if
compiling
.
returncode
!=
0
:
if
compiling
.
returncode
!=
0
:
stdout_captured
=
None
# Don't save erroneous result.
stdout_captured
=
None
# Don't save erroneous result.
raise
CompilerError
(
raise
CompilerError
(
"{0!r} exit code {1}
\n
{2}"
.
format
(
argument_list
,
compiling
.
returncode
,
stderr
))
"{0!r} exit code {1}
\n
{2}"
.
format
(
argument_list
,
compiling
.
returncode
,
stderr
),
command
=
argument_list
,
error_output
=
stderr
)
# User wants to see everything that happened.
# User wants to see everything that happened.
if
self
.
verbose
:
if
self
.
verbose
:
...
@@ -134,7 +136,8 @@ class SubProcessCompiler(CompilerBase):
...
@@ -134,7 +136,8 @@ class SubProcessCompiler(CompilerBase):
print
(
stderr
)
print
(
stderr
)
except
OSError
as
e
:
except
OSError
as
e
:
stdout_captured
=
None
# Don't save erroneous result.
stdout_captured
=
None
# Don't save erroneous result.
raise
CompilerError
(
e
)
raise
CompilerError
(
e
,
command
=
argument_list
,
error_output
=
text_type
(
e
))
finally
:
finally
:
# Decide what to do with captured stdout.
# Decide what to do with captured stdout.
if
stdout
:
if
stdout
:
...
...
pipeline/conf.py
View file @
452f97f9
...
@@ -23,6 +23,8 @@ DEFAULTS = {
...
@@ -23,6 +23,8 @@ DEFAULTS = {
'PIPELINE_ROOT'
:
_settings
.
STATIC_ROOT
,
'PIPELINE_ROOT'
:
_settings
.
STATIC_ROOT
,
'PIPELINE_URL'
:
_settings
.
STATIC_URL
,
'PIPELINE_URL'
:
_settings
.
STATIC_URL
,
'SHOW_ERRORS_INLINE'
:
_settings
.
DEBUG
,
'CSS_COMPRESSOR'
:
'pipeline.compressors.yuglify.YuglifyCompressor'
,
'CSS_COMPRESSOR'
:
'pipeline.compressors.yuglify.YuglifyCompressor'
,
'JS_COMPRESSOR'
:
'pipeline.compressors.yuglify.YuglifyCompressor'
,
'JS_COMPRESSOR'
:
'pipeline.compressors.yuglify.YuglifyCompressor'
,
'COMPILERS'
:
[],
'COMPILERS'
:
[],
...
...
pipeline/exceptions.py
View file @
452f97f9
...
@@ -10,7 +10,11 @@ class PackageNotFound(PipelineException):
...
@@ -10,7 +10,11 @@ class PackageNotFound(PipelineException):
class
CompilerError
(
PipelineException
):
class
CompilerError
(
PipelineException
):
pass
def
__init__
(
self
,
msg
,
command
=
None
,
error_output
=
None
):
super
(
CompilerError
,
self
)
.
__init__
(
msg
)
self
.
command
=
command
self
.
error_output
=
error_output
.
strip
()
class
CompressorError
(
PipelineException
):
class
CompressorError
(
PipelineException
):
...
...
pipeline/jinja2/__init__.py
View file @
452f97f9
...
@@ -34,7 +34,7 @@ class PipelineExtension(PipelineMixin, Extension):
...
@@ -34,7 +34,7 @@ class PipelineExtension(PipelineMixin, Extension):
package
=
self
.
package_for
(
package_name
,
'css'
)
package
=
self
.
package_for
(
package_name
,
'css'
)
except
PackageNotFound
:
except
PackageNotFound
:
return
''
# fail silently, do not return anything if an invalid group is specified
return
''
# fail silently, do not return anything if an invalid group is specified
return
self
.
render_compressed
(
package
,
'css'
)
return
self
.
render_compressed
(
package
,
package_name
,
'css'
)
def
render_css
(
self
,
package
,
path
):
def
render_css
(
self
,
package
,
path
):
template_name
=
package
.
template_name
or
"pipeline/css.jinja"
template_name
=
package
.
template_name
or
"pipeline/css.jinja"
...
@@ -55,7 +55,7 @@ class PipelineExtension(PipelineMixin, Extension):
...
@@ -55,7 +55,7 @@ class PipelineExtension(PipelineMixin, Extension):
package
=
self
.
package_for
(
package_name
,
'js'
)
package
=
self
.
package_for
(
package_name
,
'js'
)
except
PackageNotFound
:
except
PackageNotFound
:
return
''
# fail silently, do not return anything if an invalid group is specified
return
''
# fail silently, do not return anything if an invalid group is specified
return
self
.
render_compressed
(
package
,
'js'
)
return
self
.
render_compressed
(
package
,
package_name
,
'js'
)
def
render_js
(
self
,
package
,
path
):
def
render_js
(
self
,
package
,
path
):
template_name
=
package
.
template_name
or
"pipeline/js.jinja"
template_name
=
package
.
template_name
or
"pipeline/js.jinja"
...
...
pipeline/templates/pipeline/compile_error.html
0 → 100644
View file @
452f97f9
<div
id=
"django-pipeline-error-{{package_name}}"
class=
"django-pipeline-error"
style=
"display: none; border: 2px #DD0000 solid; margin: 1em; padding: 1em; background: white;"
>
<h1>
Error compiling {{package_type}} package "{{package_name}}"
</h1>
<p><strong>
Command:
</strong></p>
<pre
style=
"white-space: pre-wrap;"
>
{{command}}
</pre>
<p><strong>
Errors:
</strong></p>
<pre
style=
"white-space: pre-wrap;"
>
{{errors}}
</pre>
</div>
<script>
document
.
addEventListener
(
'readystatechange'
,
function
()
{
var
el
,
container
;
if
(
document
.
readyState
!==
'interactive'
)
{
return
;
}
el
=
document
.
getElementById
(
'django-pipeline-error-{{package_name}}'
);
container
=
document
.
getElementById
(
'django-pipeline-errors'
);
if
(
!
container
)
{
container
=
document
.
createElement
(
'div'
);
container
.
id
=
'django-pipeline-errors'
;
document
.
body
.
insertBefore
(
container
,
document
.
body
.
firstChild
);
}
container
.
appendChild
(
el
);
el
.
style
.
display
=
'block'
;
});
</script>
pipeline/templatetags/pipeline.py
View file @
452f97f9
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
import
logging
import
logging
import
subprocess
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
django
import
template
from
django
import
template
from
django.template.base
import
VariableDoesNotExist
from
django.template.base
import
Context
,
VariableDoesNotExist
from
django.template.loader
import
render_to_string
from
django.template.loader
import
render_to_string
from
django.utils.safestring
import
mark_safe
from
django.utils.safestring
import
mark_safe
from
..collector
import
default_collector
from
..collector
import
default_collector
from
..conf
import
settings
from
..conf
import
settings
from
..exceptions
import
CompilerError
from
..packager
import
Packager
,
PackageNotFound
from
..packager
import
Packager
,
PackageNotFound
from
..utils
import
guess_type
from
..utils
import
guess_type
...
@@ -51,7 +53,7 @@ class PipelineMixin(object):
...
@@ -51,7 +53,7 @@ class PipelineMixin(object):
except
VariableDoesNotExist
:
except
VariableDoesNotExist
:
pass
pass
def
render_compressed
(
self
,
package
,
package_type
):
def
render_compressed
(
self
,
package
,
package_
name
,
package_
type
):
if
settings
.
PIPELINE_ENABLED
:
if
settings
.
PIPELINE_ENABLED
:
method
=
getattr
(
self
,
"render_{0}"
.
format
(
package_type
))
method
=
getattr
(
self
,
"render_{0}"
.
format
(
package_type
))
return
method
(
package
,
package
.
output_filename
)
return
method
(
package
,
package
.
output_filename
)
...
@@ -61,10 +63,29 @@ class PipelineMixin(object):
...
@@ -61,10 +63,29 @@ class PipelineMixin(object):
packager
=
Packager
()
packager
=
Packager
()
method
=
getattr
(
self
,
"render_individual_{0}"
.
format
(
package_type
))
method
=
getattr
(
self
,
"render_individual_{0}"
.
format
(
package_type
))
paths
=
packager
.
compile
(
package
.
paths
)
try
:
paths
=
packager
.
compile
(
package
.
paths
)
except
CompilerError
as
e
:
if
settings
.
SHOW_ERRORS_INLINE
:
method
=
getattr
(
self
,
'render_error_{0}'
.
format
(
package_type
))
return
method
(
package_name
,
e
)
else
:
raise
templates
=
packager
.
pack_templates
(
package
)
templates
=
packager
.
pack_templates
(
package
)
return
method
(
package
,
paths
,
templates
=
templates
)
return
method
(
package
,
paths
,
templates
=
templates
)
def
render_error
(
self
,
package_type
,
package_name
,
e
):
return
render_to_string
(
'pipeline/compile_error.html'
,
Context
({
'package_type'
:
package_type
,
'package_name'
:
package_name
,
'command'
:
subprocess
.
list2cmdline
(
e
.
command
),
'errors'
:
e
.
error_output
,
}))
class
StylesheetNode
(
PipelineMixin
,
template
.
Node
):
class
StylesheetNode
(
PipelineMixin
,
template
.
Node
):
def
__init__
(
self
,
name
):
def
__init__
(
self
,
name
):
...
@@ -79,7 +100,7 @@ class StylesheetNode(PipelineMixin, template.Node):
...
@@ -79,7 +100,7 @@ class StylesheetNode(PipelineMixin, template.Node):
except
PackageNotFound
:
except
PackageNotFound
:
logger
.
warn
(
"Package
%
r is unknown. Check PIPELINE_CSS in your settings."
,
package_name
)
logger
.
warn
(
"Package
%
r is unknown. Check PIPELINE_CSS in your settings."
,
package_name
)
return
''
# fail silently, do not return anything if an invalid group is specified
return
''
# fail silently, do not return anything if an invalid group is specified
return
self
.
render_compressed
(
package
,
'css'
)
return
self
.
render_compressed
(
package
,
package_name
,
'css'
)
def
render_css
(
self
,
package
,
path
):
def
render_css
(
self
,
package
,
path
):
template_name
=
package
.
template_name
or
"pipeline/css.html"
template_name
=
package
.
template_name
or
"pipeline/css.html"
...
@@ -94,6 +115,10 @@ class StylesheetNode(PipelineMixin, template.Node):
...
@@ -94,6 +115,10 @@ class StylesheetNode(PipelineMixin, template.Node):
tags
=
[
self
.
render_css
(
package
,
path
)
for
path
in
paths
]
tags
=
[
self
.
render_css
(
package
,
path
)
for
path
in
paths
]
return
'
\n
'
.
join
(
tags
)
return
'
\n
'
.
join
(
tags
)
def
render_error_css
(
self
,
package_name
,
e
):
return
super
(
StylesheetNode
,
self
)
.
render_error
(
'CSS'
,
package_name
,
e
)
class
JavascriptNode
(
PipelineMixin
,
template
.
Node
):
class
JavascriptNode
(
PipelineMixin
,
template
.
Node
):
def
__init__
(
self
,
name
):
def
__init__
(
self
,
name
):
...
@@ -108,7 +133,7 @@ class JavascriptNode(PipelineMixin, template.Node):
...
@@ -108,7 +133,7 @@ class JavascriptNode(PipelineMixin, template.Node):
except
PackageNotFound
:
except
PackageNotFound
:
logger
.
warn
(
"Package
%
r is unknown. Check PIPELINE_JS in your settings."
,
package_name
)
logger
.
warn
(
"Package
%
r is unknown. Check PIPELINE_JS in your settings."
,
package_name
)
return
''
# fail silently, do not return anything if an invalid group is specified
return
''
# fail silently, do not return anything if an invalid group is specified
return
self
.
render_compressed
(
package
,
'js'
)
return
self
.
render_compressed
(
package
,
package_name
,
'js'
)
def
render_js
(
self
,
package
,
path
):
def
render_js
(
self
,
package
,
path
):
template_name
=
package
.
template_name
or
"pipeline/js.html"
template_name
=
package
.
template_name
or
"pipeline/js.html"
...
@@ -132,6 +157,10 @@ class JavascriptNode(PipelineMixin, template.Node):
...
@@ -132,6 +157,10 @@ class JavascriptNode(PipelineMixin, template.Node):
tags
.
append
(
self
.
render_inline
(
package
,
templates
))
tags
.
append
(
self
.
render_inline
(
package
,
templates
))
return
'
\n
'
.
join
(
tags
)
return
'
\n
'
.
join
(
tags
)
def
render_error_js
(
self
,
package_name
,
e
):
return
super
(
JavascriptNode
,
self
)
.
render_error
(
'JavaScript'
,
package_name
,
e
)
@register.tag
@register.tag
def
stylesheet
(
parser
,
token
):
def
stylesheet
(
parser
,
token
):
...
...
tests/tests/test_compiler.py
View file @
452f97f9
...
@@ -156,7 +156,16 @@ class InvalidCompilerTest(TestCase):
...
@@ -156,7 +156,16 @@ class InvalidCompilerTest(TestCase):
self
.
compiler
=
Compiler
()
self
.
compiler
=
Compiler
()
def
test_compile
(
self
):
def
test_compile
(
self
):
self
.
assertRaises
(
CompilerError
,
self
.
compiler
.
compile
,
[
_
(
'pipeline/js/dummy.coffee'
)])
with
self
.
assertRaises
(
CompilerError
)
as
cm
:
self
.
compiler
.
compile
([
_
(
'pipeline/js/dummy.coffee'
)])
e
=
cm
.
exception
self
.
assertEqual
(
e
.
command
,
[
'this-exists-nowhere-as-a-command-and-should-fail'
,
'pipeline/js/dummy.coffee'
,
'pipeline/js/dummy.junk'
])
self
.
assertEqual
(
e
.
error_output
,
''
)
def
tearDown
(
self
):
def
tearDown
(
self
):
default_collector
.
clear
()
default_collector
.
clear
()
...
@@ -170,7 +179,12 @@ class FailingCompilerTest(TestCase):
...
@@ -170,7 +179,12 @@ class FailingCompilerTest(TestCase):
self
.
compiler
=
Compiler
()
self
.
compiler
=
Compiler
()
def
test_compile
(
self
):
def
test_compile
(
self
):
self
.
assertRaises
(
CompilerError
,
self
.
compiler
.
compile
,
[
_
(
'pipeline/js/dummy.coffee'
)])
with
self
.
assertRaises
(
CompilerError
)
as
cm
:
self
.
compiler
.
compile
([
_
(
'pipeline/js/dummy.coffee'
)])
e
=
cm
.
exception
self
.
assertEqual
(
e
.
command
,
[
'/usr/bin/env'
,
'false'
])
self
.
assertEqual
(
e
.
error_output
,
''
)
def
tearDown
(
self
):
def
tearDown
(
self
):
default_collector
.
clear
()
default_collector
.
clear
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment