From 64dcfbed1d763781934f7d968318bf15a12d6844 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Sun, 22 Dec 2019 20:48:12 +0000 Subject: [PATCH] Replace uses of exec() in module loader Improve the module loader to avoid the more dangerous exec() function in favor of smarter use of importlib. This allows us to drop some flake8 whitelisting. Also switch out the hard-coded module list (which missed addition of the daemon module) with a more dynamic routine on Python 3.7 and later, which will use all *.py files in the top level of the package so none are forgotten in the future. Keep a hard-coded fallback list for support of earlier Python versions, but compare the two so this list is guaranteed complete for as long as it is maintained. --- mudpy/__init__.py | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/mudpy/__init__.py b/mudpy/__init__.py index 59128e8..3a8cf43 100644 --- a/mudpy/__init__.py +++ b/mudpy/__init__.py @@ -1,33 +1,62 @@ """Core modules package for the mudpy engine.""" -# Copyright (c) 2004-2018 mudpy authors. Permission to use, copy, +# Copyright (c) 2004-2019 mudpy authors. Permission to use, copy, # modify, and distribute this software is granted under terms # provided in the LICENSE file distributed with this software. -import importlib # noqa (referenced via exec of string literal below) +import importlib -import mudpy # noqa (referenced via exec of string literal below) +import mudpy def load(): """Import/reload some modules (be careful, as this can result in loops).""" - # pick up the modules list from this package - global modules - - # iterate over the list of modules provided + # hard-coded fallback list of modules expected in this package + # TODO(fungi) remove this once Python 3.6 is no longer supported + fallback_modules = [ + "command", + "daemon", + "data", + "misc", + "password", + "telnet", + "version", + ] + try: + # dynamically build module list from package contents (this only works + # in Python 3.7 and later, hence the try/except) + modules = [] + for module in mudpy.__loader__.contents(): + + if ( + # make sure it's a module file, not a directory + module.endswith('.py') + + # don't include this file, we're inside it + and module != '__init__.py'): + + # trim off the .py file extension + modules.append(module[:-3]) + + # make sure the fallback list is kept up to date with package contents + if fallback_modules != sorted(modules): + raise Exception("Fallback module list is incomplete") + + except AttributeError: + modules = fallback_modules + + # iterate over the list of module files included in the package for module in modules: # attempt to reload the module, assuming it was probably imported # earlier try: - exec("importlib.reload(%s)" % module) + importlib.reload(getattr(mudpy, module)) # must not have been, so import it now - except NameError: - exec("import mudpy.%s" % module) + except AttributeError: + importlib.import_module("mudpy.%s" % module) -# load the modules contained in this package -modules = ["command", "data", "misc", "password", "telnet", "version"] load() -- 2.11.0