From 240fe70902a305bd28bd244ac863477e953df4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 24 Jul 2015 12:15:36 +0200 Subject: [PATCH 1/4] Custom try-catch block for jinja Allows some more error checking in templates. Not used at the moment but might come in handy in the future. (cherry picked from commit 7708eea) --- src/octoprint/server/__init__.py | 1 + src/octoprint/server/util/jinja.py | 60 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/octoprint/server/util/jinja.py diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 0b2e1206..b0a952e7 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -681,6 +681,7 @@ class Server(object): import re app.jinja_env.add_extension("jinja2.ext.do") + app.jinja_env.add_extension("octoprint.server.util.jinja.trycatch") def regex_replace(s, find, replace): return re.sub(find, replace, s) diff --git a/src/octoprint/server/util/jinja.py b/src/octoprint/server/util/jinja.py new file mode 100644 index 00000000..40208d04 --- /dev/null +++ b/src/octoprint/server/util/jinja.py @@ -0,0 +1,60 @@ +# coding=utf-8 +from __future__ import absolute_import + +__author__ = "Gina Häußge " +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" + +from jinja2 import nodes +from jinja2.ext import Extension + +import logging + +class ExceptionHandlerExtension(Extension): + tags = {"try"} + + def __init__(self, environment): + super(ExceptionHandlerExtension, self).__init__(environment) + self._logger = logging.getLogger(__name__) + + def parse(self, parser): + token = parser.stream.next() + lineno = token.lineno + filename = parser.name + error = parser.parse_expression() + + args = [error, nodes.Const(filename), nodes.Const(lineno)] + try: + body = parser.parse_statements(["name:endtry"], drop_needle=True) + node = nodes.CallBlock(self.call_method("_handle_body", args), + [], [], body).set_lineno(lineno) + except Exception as e: + # that was expected + self._logger.exception("Caught exception while parsing template") + node = nodes.CallBlock(self.call_method("_handle_error", [nodes.Const(self._format_error(error, e, filename, lineno))]), + [], [], []).set_lineno(lineno) + + return node + + def _handle_body(self, error, filename, lineno, caller): + try: + return caller() + except Exception as e: + self._logger.exception("Caught exception while compiling template {filename} at line {lineno}".format(**locals())) + error_string = self._format_error(error, e, filename, lineno) + return error_string if error_string else "" + + def _handle_error(self, error, caller): + return error if error else "" + + def _format_error(self, error, exception, filename, lineno): + if not error: + return "" + + try: + return error.format(exception=exception, filename=filename, lineno=lineno) + except: + self._logger.exception("Error while compiling exception output for template {filename} at line {lineno}".format(**locals())) + return "Unknown error" + +trycatch = ExceptionHandlerExtension From 162c4f11dd36daa89c699834ce2bdddcdfbfd2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 19 Nov 2015 19:08:10 +0100 Subject: [PATCH 2/4] Moved jinja ExceptionHandlerExtension to octoprint.util.jinja (cherry picked from commit 3d0fc8d) --- src/octoprint/server/util/jinja.py | 60 ------------------------------ src/octoprint/util/jinja.py | 53 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 60 deletions(-) delete mode 100644 src/octoprint/server/util/jinja.py diff --git a/src/octoprint/server/util/jinja.py b/src/octoprint/server/util/jinja.py deleted file mode 100644 index 40208d04..00000000 --- a/src/octoprint/server/util/jinja.py +++ /dev/null @@ -1,60 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import - -__author__ = "Gina Häußge " -__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' -__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" - -from jinja2 import nodes -from jinja2.ext import Extension - -import logging - -class ExceptionHandlerExtension(Extension): - tags = {"try"} - - def __init__(self, environment): - super(ExceptionHandlerExtension, self).__init__(environment) - self._logger = logging.getLogger(__name__) - - def parse(self, parser): - token = parser.stream.next() - lineno = token.lineno - filename = parser.name - error = parser.parse_expression() - - args = [error, nodes.Const(filename), nodes.Const(lineno)] - try: - body = parser.parse_statements(["name:endtry"], drop_needle=True) - node = nodes.CallBlock(self.call_method("_handle_body", args), - [], [], body).set_lineno(lineno) - except Exception as e: - # that was expected - self._logger.exception("Caught exception while parsing template") - node = nodes.CallBlock(self.call_method("_handle_error", [nodes.Const(self._format_error(error, e, filename, lineno))]), - [], [], []).set_lineno(lineno) - - return node - - def _handle_body(self, error, filename, lineno, caller): - try: - return caller() - except Exception as e: - self._logger.exception("Caught exception while compiling template {filename} at line {lineno}".format(**locals())) - error_string = self._format_error(error, e, filename, lineno) - return error_string if error_string else "" - - def _handle_error(self, error, caller): - return error if error else "" - - def _format_error(self, error, exception, filename, lineno): - if not error: - return "" - - try: - return error.format(exception=exception, filename=filename, lineno=lineno) - except: - self._logger.exception("Error while compiling exception output for template {filename} at line {lineno}".format(**locals())) - return "Unknown error" - -trycatch = ExceptionHandlerExtension diff --git a/src/octoprint/util/jinja.py b/src/octoprint/util/jinja.py index ac2d01ba..1eb31126 100644 --- a/src/octoprint/util/jinja.py +++ b/src/octoprint/util/jinja.py @@ -4,8 +4,11 @@ from __future__ import absolute_import __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" +import logging import os +from jinja2 import nodes +from jinja2.ext import Extension from jinja2.loaders import FileSystemLoader, PrefixLoader, ChoiceLoader, \ TemplateNotFound, split_template_path @@ -106,3 +109,53 @@ def get_all_asset_paths(env): # intentionally ignored pass return result + + +class ExceptionHandlerExtension(Extension): + tags = {"try"} + + def __init__(self, environment): + super(ExceptionHandlerExtension, self).__init__(environment) + self._logger = logging.getLogger(__name__) + + def parse(self, parser): + token = parser.stream.next() + lineno = token.lineno + filename = parser.name + error = parser.parse_expression() + + args = [error, nodes.Const(filename), nodes.Const(lineno)] + try: + body = parser.parse_statements(["name:endtry"], drop_needle=True) + node = nodes.CallBlock(self.call_method("_handle_body", args), + [], [], body).set_lineno(lineno) + except Exception as e: + # that was expected + self._logger.exception("Caught exception while parsing template") + node = nodes.CallBlock(self.call_method("_handle_error", [nodes.Const(self._format_error(error, e, filename, lineno))]), + [], [], []).set_lineno(lineno) + + return node + + def _handle_body(self, error, filename, lineno, caller): + try: + return caller() + except Exception as e: + self._logger.exception("Caught exception while compiling template {filename} at line {lineno}".format(**locals())) + error_string = self._format_error(error, e, filename, lineno) + return error_string if error_string else "" + + def _handle_error(self, error, caller): + return error if error else "" + + def _format_error(self, error, exception, filename, lineno): + if not error: + return "" + + try: + return error.format(exception=exception, filename=filename, lineno=lineno) + except: + self._logger.exception("Error while compiling exception output for template {filename} at line {lineno}".format(**locals())) + return "Unknown error" + +trycatch = ExceptionHandlerExtension From 5126fc10936cef1e53e9dde5f47ed51dc9f95279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 20 Nov 2015 11:46:28 +0100 Subject: [PATCH 3/4] Fixed an import error introduced through moving the jinja trycatch extension (cherry picked from commit 3ea77c7) --- src/octoprint/server/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index b0a952e7..34dbf07a 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -681,7 +681,7 @@ class Server(object): import re app.jinja_env.add_extension("jinja2.ext.do") - app.jinja_env.add_extension("octoprint.server.util.jinja.trycatch") + app.jinja_env.add_extension("octoprint.util.jinja.trycatch") def regex_replace(s, find, replace): return re.sub(find, replace, s) From 924d5c8d7d5a44d17544a0a98519ab41a4b7f851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 6 Jul 2016 15:09:38 +0200 Subject: [PATCH 4/4] More error resilience in template rendering Wrapped potentially external and problematic includes in custom try-endtry-blocks --- src/octoprint/templates/dialogs/about.jinja2 | 2 +- src/octoprint/templates/dialogs/settings.jinja2 | 2 +- src/octoprint/templates/dialogs/usersettings.jinja2 | 2 +- src/octoprint/templates/index.jinja2 | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/octoprint/templates/dialogs/about.jinja2 b/src/octoprint/templates/dialogs/about.jinja2 index a33e85d0..dc94bb2c 100644 --- a/src/octoprint/templates/dialogs/about.jinja2 +++ b/src/octoprint/templates/dialogs/about.jinja2 @@ -38,7 +38,7 @@ class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}" {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %} > - {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %} {% if "custom_bindings" not in data or data["custom_bindings"] %}{% endif %} {% endif %} diff --git a/src/octoprint/templates/dialogs/settings.jinja2 b/src/octoprint/templates/dialogs/settings.jinja2 index 8187512e..ca95942a 100644 --- a/src/octoprint/templates/dialogs/settings.jinja2 +++ b/src/octoprint/templates/dialogs/settings.jinja2 @@ -38,7 +38,7 @@ class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}" {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %} > - {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %} {% if "custom_bindings" not in data or data["custom_bindings"] %}{% endif %} {% endif %} diff --git a/src/octoprint/templates/dialogs/usersettings.jinja2 b/src/octoprint/templates/dialogs/usersettings.jinja2 index be405c15..d34da7b4 100644 --- a/src/octoprint/templates/dialogs/usersettings.jinja2 +++ b/src/octoprint/templates/dialogs/usersettings.jinja2 @@ -35,7 +35,7 @@ class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}" {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %} > - {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %} {% if "custom_bindings" not in data or data["custom_bindings"] %}{% endif %} {% endif %} diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 9659de66..5399fb4d 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -31,7 +31,7 @@ {% if "classes" in data %}class="{{ data.classes|join(' ') }}"{% endif %} {% if "styles" in data %}style="{{ data.styles|join(', ') }}"{% endif %} > - {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %} {% if "custom_bindings" not in data or data["custom_bindings"] %}{% endif %} {% endfor %} @@ -58,7 +58,7 @@ {% if "icon" in data %} {% endif %}{{ entry|e }} {% if "template_header" in data %} - {% include data.template_header ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template_header ignore missing %}{% endtry %} {% endif %}
- {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %}
@@ -102,7 +102,7 @@ {% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %} {% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %} > - {% include data.template ignore missing %} + {% try "There was an error with the template {filename} at line number {lineno}: {exception}" %}{% include data.template ignore missing %}{% endtry %} {% if "custom_bindings" not in data or data["custom_bindings"] %}{% endif %} {% endfor %} @@ -142,7 +142,7 @@ {% for key in templates.generic.order %} {% set data = templates.generic.entries[key] %} - {% include data.template ignore missing %} + {% try "" %}{% include data.template ignore missing %}{% endtry %} {% endfor %}