diff --git a/apprise/utils.py b/apprise/utils.py index 60db30b1..561a5a23 100644 --- a/apprise/utils.py +++ b/apprise/utils.py @@ -63,7 +63,13 @@ def import_module(path, name): except Exception as e: # module isn't loadable - del sys.modules[name] + try: + del sys.modules[name] + + except KeyError: + # nothing to clean up + pass + module = None logger.debug( @@ -200,6 +206,9 @@ UUID4_RE = re.compile( r'[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}', re.IGNORECASE) +# Validate if we're a loadable Python file or not +VALID_PYTHON_FILE_RE = re.compile(r'.+\.py(o|c)?$', re.IGNORECASE) + # validate_regex() utilizes this mapping to track and re-use pre-complied # regular expressions REGEX_VALIDATE_LOOKUP = {} @@ -1565,6 +1574,11 @@ def module_detection(paths, cache=True): # Since our plugin name can conflict (as a module) with another # we want to generate random strings to avoid steping on # another's namespace + if not (path and VALID_PYTHON_FILE_RE.match(path)): + # Ignore file/module type + logger.trace('Plugin Scan: Skipping %s', path) + return None + module_name = hashlib.sha1(path.encode('utf-8')).hexdigest() module_pyname = "{prefix}.{name}".format( prefix='apprise.custom.module', name=module_name) diff --git a/test/test_apprise_utils.py b/test/test_apprise_utils.py index 6cf2eb3d..14cb6a5f 100644 --- a/test/test_apprise_utils.py +++ b/test/test_apprise_utils.py @@ -2015,6 +2015,21 @@ def test_parse_list(): ]) +def test_import_module(tmpdir): + """utils: imort_module testing + """ + # Prepare ourselves a file to work with + bad_file_base = tmpdir.mkdir('a') + bad_file = bad_file_base.join('README.md') + bad_file.write(cleandoc(""" + I'm a README file, not a Python one. + + I can't be loaded + """)) + assert utils.import_module(str(bad_file), 'invalidfile1') is None + assert utils.import_module(str(bad_file_base), 'invalidfile2') is None + + def test_module_detection(tmpdir): """utils: test_module_detection() testing """ @@ -2041,13 +2056,20 @@ def test_module_detection(tmpdir): pass """)) + notify_ignore = notify_hook_a_base.join('README.md') + notify_ignore.write(cleandoc(""" + We're not a .py file, so this file gets gracefully skipped + """)) + # Not previously loaded assert 'clihook' not in common.NOTIFY_SCHEMA_MAP # load entry by string utils.module_detection(str(notify_hook_a)) + utils.module_detection(str(notify_ignore)) + utils.module_detection(str(notify_hook_a_base)) - assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 1 + assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 3 assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1 # Now loaded @@ -2057,7 +2079,7 @@ def test_module_detection(tmpdir): utils.module_detection([str(notify_hook_a)]) # No changes to our path - assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 1 + assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 3 assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1 # Reset our variables for the next test