Source code for overwatch.webApp.utilities
#!/usr/bin/env python
""" Web app specific utilities.
In particular, it handles tasks related to deployment and minimization which are not relevant
to other Overwatch packages.
.. codeauthor:: Raymond Ehlers <raymond.ehlers@cern.ch>, Yale University
"""
import os
import subprocess
import logging
logger = logging.getLogger(__name__)
# Webassets
import webassets.filter
# Configuration
from ..base import config
(serverParameters, filesRead) = config.readConfig(config.configurationType.webApp)
[docs]class PolymerBundler(webassets.filter.ExternalTool):
""" Filter to bundle Polymer html imports into a single file for deployment.
Best practices dictate that the Polymer html imports should be combined into a single file
to reduce the number of individual http requests. Polymer provides a tool to do so, called
``polymer-bundler``. By taking advantage of ``webassets``, we can automatically combine and
minimize these files when starting a web app deployment.
To successfully define the filter, the following details must be addressed:
- polymer-bundler must only be executed with relative paths, so we cannot use
``ExternalTool.subprocess``, since that gives absolute paths.
- To ensure that the polymer components also work when not bundled, the filter must be
executed in a directory above the static dir.
These issues causes quite some complications! See the ``input(...)`` function for how to deal
with these issues.
When ``webassets`` is run in debug mode, this filter will not be run! Instead, the standard
(un-minified) version will be included. For information on forcing this filter to be run,
see the :doc:`web app README </webAppReadme>`.
"""
# Define the name of the bundle so it can be referenced.
name = "PolymerBundler"
[docs] def input(self, _in, out, **kwargs):
""" Plugin function for adding an external filter to ``webassets``.
As of August 2018, the ``kwargs`` options available include:
.. code-block:: python
kwargs = {'output': 'gen/polymerBundle.html',
'output_path': '/pathToOverwatch/overwatch/webApp/static/gen/polymerBundle.html',
'source_path': '/pathToOverwatch/overwatch/webApp/static/polymerComponents.html',
'source': 'polymerComponents.html'}
Note:
``polymer-bundler`` parses arguments a bit strangely - values such as paths still need
to be in a separate argument. Thus, the arguments looks more split than would usually
be expected.
Args:
_in (StringIO): Input for the filter. Not used here.
out (StringIO): Output for the filter. The output for ``polymer-bundler`` is written here.
This will eventually be written out to a file.
**kwargs (dict): Additional options required to run the filter properly. See the function
description for the available options.
Returns:
None
"""
# Printed because otherwise we won't be able to see the output.
logger.debug("polymer-bundler filter arguments. _in: {}, out: {}, kwargs: {}".format(_in, out, kwargs))
# Cannot just use the naive current path since this could be executed from anywhere. Instead,
# look for the static folder - it must be included somewhere.
output_path = "{output_path}".format(**kwargs)
executionPath = output_path[:output_path.find(serverParameters["staticFolder"])]
# Stream the result to stdout since writing the file seems to cause trouble with
# the "out" string, which will overwrite the desired output
args = [
"polymer-bundler",
"--inline-scripts",
"--strip-comments",
#"--out-html",
#os.path.join(serverParameters["staticFolder"], "{output}.tmp".format(**kwargs)),
# NOTE: It appears that ``--in-html`` is not a valid option anyonre. The input file should just be the last argument.
os.path.join(serverParameters["staticFolder"], "{source}".format(**kwargs))
]
logger.debug("Executing polymer filter with execution path \"{executionPath}\" and args {args}".format(executionPath = executionPath, args = args))
output = subprocess.check_output(args, cwd = executionPath)
if len(output) > 0:
logger.debug("Received non-zero output string! This means the polymer-bundler filter likely worked!")
# Write the output to the out string, which will then eventually automatically be written to file
# Without explicit decoding here, it will fail
out.write(output.decode('utf-8'))
# Register filter so it can be run in the web app
webassets.filter.register_filter(PolymerBundler)