diff --git a/IPython/html/tasks.py b/IPython/html/tasks.py index 4daf9f4..1eda695 100644 --- a/IPython/html/tasks.py +++ b/IPython/html/tasks.py @@ -1,10 +1,16 @@ """invoke task file to build CSS""" -from invoke import task, run +from __future__ import print_function + import os +import sys from distutils.version import LooseVersion as V from subprocess import check_output +from invoke import task, run +from invoke.runner import Result +from invoke.exceptions import Failure + pjoin = os.path.join static_dir = 'static' components_dir = pjoin(static_dir, 'components') @@ -13,6 +19,18 @@ here = os.path.dirname(__file__) min_less_version = '2.0' max_less_version = '3.0' # exclusive if string + +def _fail(msg=''): + """Fail a task, logging a message to stderr + + raises a special Failure Exception from invoke. + """ + if msg: + print(msg, file=sys.stderr) + # raising a Failure allows us to avoid a traceback + # we only care about exited, but stdout, stderr, pty are required args + raise Failure(Result(stdout='', stderr='', pty=False, exited=1)) + def _need_css_update(): """Does less need to run?""" @@ -53,23 +71,30 @@ def css(minify=False, verbose=False, force=False): sourcemap = pjoin('style', "%s.min.css.map" % name) _compile_less(source, target, sourcemap, minify, verbose) + def _compile_less(source, target, sourcemap, minify=True, verbose=False): """Compile a less file by source and target relative to static_dir""" min_flag = '-x' if minify else '' ver_flag = '--verbose' if verbose else '' + install = "(npm install -g 'less@<{}')".format(max_less_version) # pin less to version number from above try: out = check_output(['lessc', '--version']) except OSError as err: - raise ValueError("Unable to find lessc. Please install lessc >= %s and < %s " \ - % (min_less_version, max_less_version)) + _fail("Unable to find lessc. Rebuilding css requires less >= {0} and < {1} {2}".format( + min_less_version, max_less_version, install + )) out = out.decode('utf8', 'replace') less_version = out.split()[1] if min_less_version and V(less_version) < V(min_less_version): - raise ValueError("lessc too old: %s < %s. Use `$ npm install less@X.Y.Z` to install a specific version of less" % (less_version, min_less_version)) + _fail("lessc too old: {} < {} {}".format( + less_version, min_less_version, install, + )) if max_less_version and V(less_version) >= V(max_less_version): - raise ValueError("lessc too new: %s >= %s. Use `$ npm install less@X.Y.Z` to install a specific version of less" % (less_version, max_less_version)) + _fail("lessc too new: {} >= {} {}".format( + less_version, max_less_version, install, + )) static_path = pjoin(here, static_dir) cwd = os.getcwd() diff --git a/setupbase.py b/setupbase.py index d32e413..a4e0d98 100644 --- a/setupbase.py +++ b/setupbase.py @@ -24,9 +24,10 @@ from distutils.command.build_scripts import build_scripts from distutils.command.install import install from distutils.command.install_scripts import install_scripts from distutils.cmd import Command +from distutils.errors import DistutilsExecError from fnmatch import fnmatch from glob import glob -from subprocess import check_call +from subprocess import Popen, PIPE from setupext import install_data_ext @@ -712,7 +713,15 @@ class CompileCSS(Command): cmd.append('--minify') if self.force: cmd.append('--force') - check_call(cmd, cwd=pjoin(repo_root, "IPython", "html")) + try: + p = Popen(cmd, cwd=pjoin(repo_root, "IPython", "html"), stderr=PIPE) + except OSError: + raise DistutilsExecError("invoke is required to rebuild css (pip install invoke)") + out, err = p.communicate() + if p.returncode: + if sys.version_info[0] >= 3: + err = err.decode('utf8', 'replace') + raise DistutilsExecError(err.strip()) class JavascriptVersion(Command): @@ -750,6 +759,7 @@ def css_js_prerelease(command, strict=True): if strict: raise else: - log.warn("Failed to build css sourcemaps: %s" % e) + log.warn("rebuilding css and sourcemaps failed (not a problem)") + log.warn(str(e)) command.run(self) return DecoratedCommand