Skip to content

Commit cc535e3

Browse files
authored
SW-1290 avoid crashing on import failure (mrbeam#1507)
* add try catch on importing octoprint_mrbeamdoc * add wraps decorator to keep the same function name so that octoprint finds the handler and adds it to flask * added tests for empty import
1 parent 295cc53 commit cc535e3

File tree

9 files changed

+102
-10
lines changed

9 files changed

+102
-10
lines changed

octoprint_mrbeam/decorator/__init__.py

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from functools import wraps
2+
3+
from octoprint_mrbeam.model import EmptyImport
4+
5+
6+
def prevent_execution_on_import_error(import_to_check, default_return=None, callable=None, params=None):
7+
def decorator(function):
8+
@wraps(function)
9+
def wrapper(*args, **kwargs):
10+
if is_empty_import(import_to_check):
11+
if callable is None:
12+
return default_return
13+
elif params is None:
14+
return callable()
15+
else:
16+
return callable(*params)
17+
18+
result = function(*args, **kwargs)
19+
return result
20+
21+
return wrapper
22+
23+
return decorator
24+
25+
26+
def is_empty_import(import_to_check):
27+
return isinstance(import_to_check, EmptyImport)

octoprint_mrbeam/model/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from octoprint_mrbeam.model.empty_import import EmptyImport
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class EmptyImport(object):
2+
3+
def __init__(self, module, default_return=None):
4+
super(EmptyImport, self).__init__()
5+
self.default_return = default_return
6+
self.error_message = 'Error occurred trying to import module {module}'.format(module=module)
7+
self.log_error()
8+
9+
def log_error(self):
10+
try:
11+
from octoprint_mrbeam import mrb_logger
12+
logger = mrb_logger("octoprint.plugins.mrbeam.model.EmptyImport")
13+
logger.error(self.error_message)
14+
except ImportError:
15+
return False
16+
return True
17+
18+
def __call__(self, **kwargs):
19+
return self.default_return
20+
21+
def __getattr__(self, name):
22+
return self

octoprint_mrbeam/rest_handler/docs_handler.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import octoprint.plugin
22
from flask import abort, send_file
3-
from octoprint_mrbeamdoc.exception.mrbeam_doc_not_found import MrBeamDocNotFoundException
4-
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
3+
4+
from octoprint_mrbeam.decorator.catch_import_error import prevent_execution_on_import_error
5+
from octoprint_mrbeam.model import EmptyImport
6+
7+
try:
8+
from octoprint_mrbeamdoc.exception.mrbeam_doc_not_found import MrBeamDocNotFoundException
9+
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
10+
except ImportError:
11+
MrBeamDocUtils = EmptyImport("from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils")
12+
MrBeamDocNotFoundException = EmptyImport(
13+
"from octoprint_mrbeamdoc.exception.mrbeam_doc_not_found import MrBeamDocNotFoundException")
514

615

716
class DocsRestHandlerMixin:
@@ -11,6 +20,7 @@ class DocsRestHandlerMixin:
1120

1221
@octoprint.plugin.BlueprintPlugin.route(
1322
"/docs/<string:model>/<string:language>/<string:doctype>.<string:extension>", methods=["GET"])
23+
@prevent_execution_on_import_error(MrBeamDocUtils, callable=abort, params=[404])
1424
def get_doc(self, model, doctype, language, extension):
1525
self._logger.debug(
1626
'Request to Model: %(model)s Doctype: %(doctype)s Language: %(language)s Extension:%(extension)s',

octoprint_mrbeam/services/burger_menu_service.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
from flask_babel import get_locale
2-
from octoprint_mrbeamdoc.enum.supported_languages import SupportedLanguage
3-
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
2+
3+
from octoprint_mrbeam.decorator.catch_import_error import prevent_execution_on_import_error
4+
from octoprint_mrbeam.model import EmptyImport
5+
6+
try:
7+
from octoprint_mrbeamdoc.enum.supported_languages import SupportedLanguage
8+
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
9+
except ImportError:
10+
MrBeamDocUtils = EmptyImport("from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils")
11+
SupportedLanguage = EmptyImport("from octoprint_mrbeamdoc.enum.supported_languages import SupportedLanguage")
412

513
from octoprint_mrbeam.model.burger_menu_model import BurgerMenuModel
614

@@ -14,6 +22,7 @@ def __init__(self, logger, document_service):
1422
self._logger = logger
1523
self._document_service = document_service
1624

25+
@prevent_execution_on_import_error(MrBeamDocUtils, default_return=BurgerMenuModel())
1726
def get_burger_menu_model(self, mrbeam_model):
1827
"""
1928
mrbeam_model String: Name of the running mrbeam_model

octoprint_mrbeam/services/settings_service.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1-
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
1+
from octoprint_mrbeam.decorator.catch_import_error import prevent_execution_on_import_error
2+
from octoprint_mrbeam.model import EmptyImport
3+
4+
try:
5+
from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils
6+
except ImportError:
7+
MrBeamDocUtils = EmptyImport("from octoprint_mrbeamdoc.utils.mrbeam_doc_utils import MrBeamDocUtils")
28

39
from octoprint_mrbeam.model.settings_model import SettingsModel, AboutModel
410

511

12+
def _empty_settings_model():
13+
settings_model = SettingsModel()
14+
settings_model.about = AboutModel()
15+
return settings_model
16+
17+
618
class SettingsService:
719
"""
820
In this class we gather all the service layer calculations needed regarding settings
@@ -12,6 +24,7 @@ def __init__(self, logger, document_service):
1224
self._logger = logger
1325
self._document_service = document_service
1426

27+
@prevent_execution_on_import_error(MrBeamDocUtils, callable=_empty_settings_model)
1528
def get_template_settings_model(self, mrbeam_model):
1629
"""
1730
mrbeam_model String: Name of the running mrbeam_model
@@ -28,8 +41,3 @@ def get_template_settings_model(self, mrbeam_model):
2841
settings_model.about = AboutModel(
2942
support_documents=[self._document_service.get_documents_for(definition) for definition in definitions])
3043
return settings_model
31-
32-
def _empty_settings_model(self):
33-
settings_model = SettingsModel()
34-
settings_model.about = AboutModel()
35-
return settings_model

tests/model/__init__.py

Whitespace-only changes.

tests/model/test_empty_import.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from unittest import TestCase
2+
3+
from octoprint_mrbeam.model import EmptyImport
4+
5+
6+
class TestEmptyImportClass(TestCase):
7+
8+
def test_random_attribute_returns_self(self):
9+
empty_import = EmptyImport("module name")
10+
self.assertIs(empty_import.documents, empty_import)
11+
12+
def test_random_method_returns_none(self):
13+
empty_import = EmptyImport("module name")
14+
result = empty_import.random_method()
15+
self.assertIs(result, None)

0 commit comments

Comments
 (0)