From d54d4357bb3916bd934286aaf68591d7d50e38dc Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 10 Mar 2021 21:31:15 +0800 Subject: [PATCH 01/29] add Application plugin type --- src/rez/application.py | 14 ++++++++++++++ src/rez/plugin_managers.py | 7 +++++++ src/rezplugins/application/__init__.py | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/rez/application.py create mode 100644 src/rezplugins/application/__init__.py diff --git a/src/rez/application.py b/src/rez/application.py new file mode 100644 index 000000000..a78a94f46 --- /dev/null +++ b/src/rez/application.py @@ -0,0 +1,14 @@ + +from rez.config import config + + +class Application(object): + """An interface for registering custom Rez application/subcommand""" + def __init__(self): + self.type_settings = config.plugins.application + self.settings = self.type_settings.get(self.name()) + + @classmethod + def name(cls): + """Return the name of the Application and rez-subcommand.""" + raise NotImplementedError diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index f6cc3f073..208b0f3f0 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -380,6 +380,12 @@ class BuildProcessPluginType(RezPluginType): type_name = "build_process" +class ApplicationPluginType(RezPluginType): + """Support for different custom Rez applications/subcommands. + """ + type_name = "application" + + plugin_manager = RezPluginManager() @@ -389,6 +395,7 @@ class BuildProcessPluginType(RezPluginType): plugin_manager.register_plugin_type(BuildSystemPluginType) plugin_manager.register_plugin_type(PackageRepositoryPluginType) plugin_manager.register_plugin_type(BuildProcessPluginType) +plugin_manager.register_plugin_type(ApplicationPluginType) # Copyright 2013-2016 Allan Johns. diff --git a/src/rezplugins/application/__init__.py b/src/rezplugins/application/__init__.py new file mode 100644 index 000000000..6dab8423a --- /dev/null +++ b/src/rezplugins/application/__init__.py @@ -0,0 +1,18 @@ +from rez.plugin_managers import extend_path +__path__ = extend_path(__path__, __name__) + + +# Copyright 2013-2016 Allan Johns. +# +# This library is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation, either +# version 3 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . From 0ab486d8f880e3344f2ab6dddcc9b612842a2231 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 10 Mar 2021 22:07:47 +0800 Subject: [PATCH 02/29] register subcommand from application plugin --- src/rez/cli/_main.py | 4 ++-- src/rez/cli/_util.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/rez/cli/_main.py b/src/rez/cli/_main.py index ec13215fe..ddce0c2a9 100644 --- a/src/rez/cli/_main.py +++ b/src/rez/cli/_main.py @@ -93,8 +93,8 @@ def setup_parser(): # add lazy subparsers subparser = parser.add_subparsers(dest='cmd', metavar='COMMAND') - for subcommand in subcommands: - module_name = "rez.cli.%s" % subcommand + for subcommand, data in subcommands.items(): + module_name = data.get('module_name', 'rez.cli.%s' % subcommand) subparser.add_parser( subcommand, diff --git a/src/rez/cli/_util.py b/src/rez/cli/_util.py index ceac613e7..e5a8b100f 100644 --- a/src/rez/cli/_util.py +++ b/src/rez/cli/_util.py @@ -60,6 +60,52 @@ } +def load_plugin_cmd(): + """Load application plugin subcommand + + The application plugin module must have attribute `command_behavior`, and + the value must be a dict. For example: + + # in your application plugin module + command_behavior = { + "hidden": False, # optional: bool + "arg_mode": None, # optional: None, "passthrough", "grouped" + } + + """ + from rez.config import config + from rez.utils.logging_ import print_debug + from rez.plugin_managers import plugin_manager + + ext_plugins = dict() + + for plugin_name in plugin_manager.get_plugins("application"): + module = plugin_manager.get_plugin_module("application", plugin_name) + + if hasattr(module, "command_behavior"): + try: + data = module.command_behavior.copy() + data.update({"module_name": module.__name__}) + ext_plugins[plugin_name] = data + + except Exception: + if config.debug("plugins"): + import traceback + from rez.vendor.six.six import StringIO + out = StringIO() + traceback.print_exc(file=out) + print_debug(out.getvalue()) + + elif config.debug("plugins"): + print_debug("Attribute 'command_behavior' not found in plugin " + "module %s, command not registered." % module.__name__) + + return ext_plugins + + +subcommands.update(load_plugin_cmd()) + + class LazySubParsersAction(_SubParsersAction): """Argparse Action which calls the `setup_subparser` function provided to `LazyArgumentParser`. From bc20be676db0d11bc710dfda35403d803c812755 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 10 Mar 2021 22:08:12 +0800 Subject: [PATCH 03/29] allow plugin installed directly with config --- src/rez/plugin_managers.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 208b0f3f0..f99f29e72 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -119,6 +119,8 @@ def load_plugins(self): if config.debug("plugins"): print_debug("searching plugin path %s...", path) + plugin_names = set() + for loader, modname, ispkg in pkgutil.iter_modules( [path], package.__name__ + '.'): @@ -126,7 +128,9 @@ def load_plugins(self): continue plugin_name = modname.split('.')[-1] - if plugin_name.startswith('_'): + if (plugin_name.startswith('_') + or plugin_name == 'rezconfig' + or plugin_name.startswith('rezconfig-')): continue if config.debug("plugins"): @@ -143,6 +147,7 @@ def load_plugins(self): plugin_class = module.register_plugin() if plugin_class != None: self.register_plugin(plugin_name, plugin_class, module) + plugin_names.add(plugin_name) else: if config.debug("plugins"): print_warning( @@ -167,7 +172,15 @@ def load_plugins(self): print_debug(out.getvalue()) # load config - data, _ = _load_config_from_filepaths([os.path.join(path, "rezconfig")]) + # accepting 'rezconfig-{plugin_name}' as valid config file is + # to allow custom plugin being pip-installed directly under + # `rezplugins` package, and to avoid default config file being + # overwritten by the custom plugin's config file during install. + default_config = [os.path.join(path, "rezconfig")] + plugins_config = [os.path.join(path, "rezconfig-%s" % n) + for n in plugin_names] + config_paths = default_config + plugins_config + data, _ = _load_config_from_filepaths(config_paths) deep_update(self.config_data, data) def get_plugin_class(self, plugin_name): From bef519834a98ee9385f9b8265025a8403b12a00e Mon Sep 17 00:00:00 2001 From: David Lai Date: Thu, 11 Mar 2021 03:20:50 +0800 Subject: [PATCH 04/29] add plugin_module for loading rezplugins form module --- src/rez/config.py | 1 + src/rez/plugin_managers.py | 26 ++++++++++++++++++++++++-- src/rez/rezconfig.py | 3 +++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/rez/config.py b/src/rez/config.py index ce858d686..87445360e 100644 --- a/src/rez/config.py +++ b/src/rez/config.py @@ -333,6 +333,7 @@ def _parse_env_var(self, value): "default_cachable_per_package": OptionalDict, "default_cachable_per_repository": OptionalDict, "default_cachable": OptionalBool, + "plugin_module": StrList, "implicit_packages": StrList, "parent_variables": StrList, "resetting_variables": StrList, diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index f99f29e72..3fbfd1fcb 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -8,6 +8,7 @@ from rez.utils.logging_ import print_debug, print_warning from rez.vendor.six import six from rez.exceptions import RezPluginError +import importlib import os.path import sys @@ -52,11 +53,11 @@ def extend_path(path, name): init_py = "__init__" + os.extsep + "py" path = path[:] - for dir_ in config.plugin_path: + def append_if_valid(dir_): if not os.path.isdir(dir_): if config.debug("plugins"): print_debug("skipped nonexistant rez plugin path: %s" % dir_) - continue + return subdir = os.path.join(dir_, pname) # XXX This may still add duplicate entries to path on @@ -65,6 +66,27 @@ def extend_path(path, name): if subdir not in path and os.path.isfile(initfile): path.append(subdir) + for dir_ in config.plugin_path: + append_if_valid(dir_) + + for name in config.plugin_module: + if name not in sys.modules: + try: + importlib.import_module(name) + except Exception: + if config.debug("plugins"): + import traceback + from rez.vendor.six.six import StringIO + out = StringIO() + traceback.print_exc(file=out) + print_debug(out.getvalue()) + continue + + module_path = sys.modules[name].__path__ + for root in module_path: + dir_ = os.path.join(root, "_plugins") + append_if_valid(dir_) + return path diff --git a/src/rez/rezconfig.py b/src/rez/rezconfig.py index ea6e64526..bc2c5498e 100644 --- a/src/rez/rezconfig.py +++ b/src/rez/rezconfig.py @@ -123,6 +123,9 @@ # Search path for rez plugins. plugin_path = [] +# Search module name for rez plugins. +plugin_module = [] + # Search path for bind modules. The *rez-bind* tool uses these modules to create # rez packages that reference existing software already installed on the system. bind_module_path = [] From 9bfa53fa660025e77c6c8a353c8ecb042acc01c3 Mon Sep 17 00:00:00 2001 From: David Lai Date: Thu, 11 Mar 2021 16:05:11 +0800 Subject: [PATCH 05/29] revert bc20be67 for plugin_module --- src/rez/plugin_managers.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 3fbfd1fcb..be322f52f 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -141,8 +141,6 @@ def load_plugins(self): if config.debug("plugins"): print_debug("searching plugin path %s...", path) - plugin_names = set() - for loader, modname, ispkg in pkgutil.iter_modules( [path], package.__name__ + '.'): @@ -150,9 +148,7 @@ def load_plugins(self): continue plugin_name = modname.split('.')[-1] - if (plugin_name.startswith('_') - or plugin_name == 'rezconfig' - or plugin_name.startswith('rezconfig-')): + if plugin_name.startswith('_') or plugin_name == 'rezconfig': continue if config.debug("plugins"): @@ -169,7 +165,6 @@ def load_plugins(self): plugin_class = module.register_plugin() if plugin_class != None: self.register_plugin(plugin_name, plugin_class, module) - plugin_names.add(plugin_name) else: if config.debug("plugins"): print_warning( @@ -194,15 +189,7 @@ def load_plugins(self): print_debug(out.getvalue()) # load config - # accepting 'rezconfig-{plugin_name}' as valid config file is - # to allow custom plugin being pip-installed directly under - # `rezplugins` package, and to avoid default config file being - # overwritten by the custom plugin's config file during install. - default_config = [os.path.join(path, "rezconfig")] - plugins_config = [os.path.join(path, "rezconfig-%s" % n) - for n in plugin_names] - config_paths = default_config + plugins_config - data, _ = _load_config_from_filepaths(config_paths) + data, _ = _load_config_from_filepaths([os.path.join(path, "rezconfig")]) deep_update(self.config_data, data) def get_plugin_class(self, plugin_name): From 6d8adc33983d56a7bf87e4f127f771b307eeacab Mon Sep 17 00:00:00 2001 From: David Lai Date: Thu, 11 Mar 2021 16:06:36 +0800 Subject: [PATCH 06/29] remove redundant hierarchy --- src/rez/plugin_managers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index be322f52f..08c108728 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -83,8 +83,7 @@ def append_if_valid(dir_): continue module_path = sys.modules[name].__path__ - for root in module_path: - dir_ = os.path.join(root, "_plugins") + for dir_ in module_path: append_if_valid(dir_) return path From 51a735a642822dd64fdfe735b5f528bd52e7aa9b Mon Sep 17 00:00:00 2001 From: David Lai Date: Sat, 13 Mar 2021 04:10:59 +0800 Subject: [PATCH 07/29] add docstring --- src/rez/application.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/rez/application.py b/src/rez/application.py index a78a94f46..c660cb2ff 100644 --- a/src/rez/application.py +++ b/src/rez/application.py @@ -3,7 +3,45 @@ class Application(object): - """An interface for registering custom Rez application/subcommand""" + """An interface for registering custom Rez subcommand + + To register plugin and expose subcommand, the plugin module.. + + * MUST have a module-level docstring (used as the command help) + * MUST have a module-level attribute `command_behavior` + * MUST provide a `setup_parser()` function + * MUST provide a `command()` function + * MUST provide a `register_plugin()` function + + For example, a plugin named 'foo' and this is the `foo.py`: + + '''The docstring for command help, this is required. + ''' + from rez.application import Application + + command_behavior = { + "hidden": False, # optional: bool + "arg_mode": None, # optional: None, "passthrough", "grouped" + } + + def setup_parser(parser, completions=False): + parser.add_argument("--hello", ...) + + def command(opts, parser=None, extra_arg_groups=None): + if opts.hello: + print("world") + + class ApplicationFoo(Application): + schema_dict = {} + + @classmethod + def name(cls): + return "foo" + + def register_plugin(): + return ApplicationFoo + + """ def __init__(self): self.type_settings = config.plugins.application self.settings = self.type_settings.get(self.name()) From 0b5be5c577595aacd99501b38849950e2a569d68 Mon Sep 17 00:00:00 2001 From: David Lai Date: Sat, 13 Mar 2021 04:27:46 +0800 Subject: [PATCH 08/29] update test --- src/rez/tests/test_completion.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rez/tests/test_completion.py b/src/rez/tests/test_completion.py index 13ce77b40..80a476cf8 100644 --- a/src/rez/tests/test_completion.py +++ b/src/rez/tests/test_completion.py @@ -29,8 +29,10 @@ def _eq(prefix, expected_completions): _eq("zzz", []) _eq("pref", ["prefix_prompt"]) _eq("plugin", ["plugins", - "plugin_path"]) + "plugin_path", + "plugin_module"]) _eq("plugins", ["plugins", + "plugins.application", "plugins.package_repository", "plugins.build_process", "plugins.build_system", From 68db98903435e7468780d54cfe9c52fa3cce6ce7 Mon Sep 17 00:00:00 2001 From: David Lai Date: Mon, 15 Mar 2021 00:36:13 +0800 Subject: [PATCH 09/29] enable completer finding plugin subcommand --- src/rez/cli/complete.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rez/cli/complete.py b/src/rez/cli/complete.py index 90b369da3..72b6e86da 100644 --- a/src/rez/cli/complete.py +++ b/src/rez/cli/complete.py @@ -80,7 +80,8 @@ def _pop_arg(l, p): # create parser for subcommand from rez.backport.importlib import import_module - module_name = "rez.cli.%s" % subcommand + data = subcommands[subcommand] + module_name = data.get("module_name", "rez.cli.%s" % subcommand) mod = import_module(module_name) parser = argparse.ArgumentParser() mod.setup_parser(parser, completions=True) From 2ec21f71f602c94b6d74c23ef4ddb0fe935f1413 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 16 Mar 2021 08:51:10 +0800 Subject: [PATCH 10/29] rename 'application' to 'extension' --- src/rez/cli/_util.py | 10 +++++----- src/rez/config.py | 2 +- src/rez/{application.py => extension.py} | 12 ++++++------ src/rez/plugin_managers.py | 8 ++++---- src/rez/rezconfig.py | 2 +- src/rez/tests/test_completion.py | 3 +-- .../{application => extension}/__init__.py | 0 7 files changed, 18 insertions(+), 19 deletions(-) rename src/rez/{application.py => extension.py} (81%) rename src/rezplugins/{application => extension}/__init__.py (100%) diff --git a/src/rez/cli/_util.py b/src/rez/cli/_util.py index e5a8b100f..b8dc989e5 100644 --- a/src/rez/cli/_util.py +++ b/src/rez/cli/_util.py @@ -61,12 +61,12 @@ def load_plugin_cmd(): - """Load application plugin subcommand + """Load extension plugin subcommand - The application plugin module must have attribute `command_behavior`, and + The extension plugin module must have attribute `command_behavior`, and the value must be a dict. For example: - # in your application plugin module + # in your extension plugin module command_behavior = { "hidden": False, # optional: bool "arg_mode": None, # optional: None, "passthrough", "grouped" @@ -79,8 +79,8 @@ def load_plugin_cmd(): ext_plugins = dict() - for plugin_name in plugin_manager.get_plugins("application"): - module = plugin_manager.get_plugin_module("application", plugin_name) + for plugin_name in plugin_manager.get_plugins("extension"): + module = plugin_manager.get_plugin_module("extension", plugin_name) if hasattr(module, "command_behavior"): try: diff --git a/src/rez/config.py b/src/rez/config.py index 87445360e..389ade572 100644 --- a/src/rez/config.py +++ b/src/rez/config.py @@ -333,7 +333,7 @@ def _parse_env_var(self, value): "default_cachable_per_package": OptionalDict, "default_cachable_per_repository": OptionalDict, "default_cachable": OptionalBool, - "plugin_module": StrList, + "extensions": StrList, "implicit_packages": StrList, "parent_variables": StrList, "resetting_variables": StrList, diff --git a/src/rez/application.py b/src/rez/extension.py similarity index 81% rename from src/rez/application.py rename to src/rez/extension.py index c660cb2ff..deb7941de 100644 --- a/src/rez/application.py +++ b/src/rez/extension.py @@ -2,7 +2,7 @@ from rez.config import config -class Application(object): +class Extension(object): """An interface for registering custom Rez subcommand To register plugin and expose subcommand, the plugin module.. @@ -17,7 +17,7 @@ class Application(object): '''The docstring for command help, this is required. ''' - from rez.application import Application + from rez.extension import Extension command_behavior = { "hidden": False, # optional: bool @@ -31,7 +31,7 @@ def command(opts, parser=None, extra_arg_groups=None): if opts.hello: print("world") - class ApplicationFoo(Application): + class ExtensionFoo(Extension): schema_dict = {} @classmethod @@ -39,14 +39,14 @@ def name(cls): return "foo" def register_plugin(): - return ApplicationFoo + return ExtensionFoo """ def __init__(self): - self.type_settings = config.plugins.application + self.type_settings = config.plugins.extension self.settings = self.type_settings.get(self.name()) @classmethod def name(cls): - """Return the name of the Application and rez-subcommand.""" + """Return the name of the Extension and rez-subcommand.""" raise NotImplementedError diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 08c108728..86fb7ac4c 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -69,7 +69,7 @@ def append_if_valid(dir_): for dir_ in config.plugin_path: append_if_valid(dir_) - for name in config.plugin_module: + for name in config.extensions: if name not in sys.modules: try: importlib.import_module(name) @@ -401,10 +401,10 @@ class BuildProcessPluginType(RezPluginType): type_name = "build_process" -class ApplicationPluginType(RezPluginType): +class ExtensionPluginType(RezPluginType): """Support for different custom Rez applications/subcommands. """ - type_name = "application" + type_name = "extension" plugin_manager = RezPluginManager() @@ -416,7 +416,7 @@ class ApplicationPluginType(RezPluginType): plugin_manager.register_plugin_type(BuildSystemPluginType) plugin_manager.register_plugin_type(PackageRepositoryPluginType) plugin_manager.register_plugin_type(BuildProcessPluginType) -plugin_manager.register_plugin_type(ApplicationPluginType) +plugin_manager.register_plugin_type(ExtensionPluginType) # Copyright 2013-2016 Allan Johns. diff --git a/src/rez/rezconfig.py b/src/rez/rezconfig.py index bc2c5498e..4a783d17f 100644 --- a/src/rez/rezconfig.py +++ b/src/rez/rezconfig.py @@ -124,7 +124,7 @@ plugin_path = [] # Search module name for rez plugins. -plugin_module = [] +extensions = [] # Search path for bind modules. The *rez-bind* tool uses these modules to create # rez packages that reference existing software already installed on the system. diff --git a/src/rez/tests/test_completion.py b/src/rez/tests/test_completion.py index 80a476cf8..0ba48f265 100644 --- a/src/rez/tests/test_completion.py +++ b/src/rez/tests/test_completion.py @@ -29,8 +29,7 @@ def _eq(prefix, expected_completions): _eq("zzz", []) _eq("pref", ["prefix_prompt"]) _eq("plugin", ["plugins", - "plugin_path", - "plugin_module"]) + "plugin_path"]) _eq("plugins", ["plugins", "plugins.application", "plugins.package_repository", diff --git a/src/rezplugins/application/__init__.py b/src/rezplugins/extension/__init__.py similarity index 100% rename from src/rezplugins/application/__init__.py rename to src/rezplugins/extension/__init__.py From 5acd7f9ebbc6672d9a9265fc188483314787883b Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 16 Mar 2021 09:31:36 +0800 Subject: [PATCH 11/29] update test --- src/rez/tests/test_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rez/tests/test_completion.py b/src/rez/tests/test_completion.py index 0ba48f265..02bc24519 100644 --- a/src/rez/tests/test_completion.py +++ b/src/rez/tests/test_completion.py @@ -31,7 +31,7 @@ def _eq(prefix, expected_completions): _eq("plugin", ["plugins", "plugin_path"]) _eq("plugins", ["plugins", - "plugins.application", + "plugins.extension", "plugins.package_repository", "plugins.build_process", "plugins.build_system", From 765fffbff1b411fba10a6d1b6f0bdd460d4d6e7f Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 16 Mar 2021 16:39:38 +0800 Subject: [PATCH 12/29] auto-load extensions via pkgutil.iter_modules --- src/rez/config.py | 1 - src/rez/plugin_managers.py | 41 ++++++++++++-------------------------- src/rez/rezconfig.py | 3 --- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/rez/config.py b/src/rez/config.py index 389ade572..ce858d686 100644 --- a/src/rez/config.py +++ b/src/rez/config.py @@ -333,7 +333,6 @@ def _parse_env_var(self, value): "default_cachable_per_package": OptionalDict, "default_cachable_per_repository": OptionalDict, "default_cachable": OptionalBool, - "extensions": StrList, "implicit_packages": StrList, "parent_variables": StrList, "resetting_variables": StrList, diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 86fb7ac4c..64d9ab95e 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -8,7 +8,7 @@ from rez.utils.logging_ import print_debug, print_warning from rez.vendor.six import six from rez.exceptions import RezPluginError -import importlib +import pkgutil import os.path import sys @@ -54,37 +54,22 @@ def extend_path(path, name): path = path[:] def append_if_valid(dir_): - if not os.path.isdir(dir_): - if config.debug("plugins"): - print_debug("skipped nonexistant rez plugin path: %s" % dir_) - return + if os.path.isdir(dir_): + subdir = os.path.normcase(os.path.join(dir_, pname)) + initfile = os.path.join(subdir, init_py) + if subdir not in path and os.path.isfile(initfile): + path.append(subdir) - subdir = os.path.join(dir_, pname) - # XXX This may still add duplicate entries to path on - # case-insensitive filesystems - initfile = os.path.join(subdir, init_py) - if subdir not in path and os.path.isfile(initfile): - path.append(subdir) + elif config.debug("plugins"): + print_debug("skipped nonexistant rez plugin path: %s" % dir_) + # Extend old-style plugins for dir_ in config.plugin_path: append_if_valid(dir_) - - for name in config.extensions: - if name not in sys.modules: - try: - importlib.import_module(name) - except Exception: - if config.debug("plugins"): - import traceback - from rez.vendor.six.six import StringIO - out = StringIO() - traceback.print_exc(file=out) - print_debug(out.getvalue()) - continue - - module_path = sys.modules[name].__path__ - for dir_ in module_path: - append_if_valid(dir_) + # Extend new-style plugins + for loader, name, ispkg in pkgutil.iter_modules(): + if ispkg: + append_if_valid(os.path.join(loader.path, name)) return path diff --git a/src/rez/rezconfig.py b/src/rez/rezconfig.py index 4a783d17f..ea6e64526 100644 --- a/src/rez/rezconfig.py +++ b/src/rez/rezconfig.py @@ -123,9 +123,6 @@ # Search path for rez plugins. plugin_path = [] -# Search module name for rez plugins. -extensions = [] - # Search path for bind modules. The *rez-bind* tool uses these modules to create # rez packages that reference existing software already installed on the system. bind_module_path = [] From 0522dd94f6e5ee726d6f119ae10bed755d9e0aad Mon Sep 17 00:00:00 2001 From: David Lai Date: Mon, 22 Mar 2021 23:12:35 +0800 Subject: [PATCH 13/29] fix #960 --- setup.py | 1 + src/rez/plugin_managers.py | 48 ++++++++++++++++++++++++++++++++------ src/rezplugins/MAIN | 3 +++ 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src/rezplugins/MAIN diff --git a/setup.py b/setup.py index 0522bd4ee..5792289fc 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,7 @@ def find_files(pattern, path=None, root="rez"): find_files('*', 'tests/data') + find_files('*.exe', 'vendor/distlib'), 'rezplugins': + ['MAIN'] + find_files('rezconfig', root='rezplugins') + find_files('*.cmake', 'build_system', root='rezplugins') + find_files('*', 'build_system/template_files', root='rezplugins'), diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 64d9ab95e..4b890e499 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -104,18 +104,18 @@ def register_plugin(self, plugin_name, plugin_class, plugin_module): self.plugin_modules[plugin_name] = plugin_module def load_plugins(self): - import pkgutil - from rez.backport.importlib import import_module - type_module_name = 'rezplugins.' + self.type_name - package = import_module(type_module_name) + main_plugin = plugin_manager.load_main_plugin_module(self.type_name) # on import, the `__path__` variable of the imported package is extended # to include existing directories on the plugin search path (via # extend_path, above). this means that `walk_packages` will walk over all # modules on the search path at the same level (.e.g in a # 'rezplugins/type_name' sub-directory). - paths = [package.__path__] if isinstance(package.__path__, basestring) \ - else package.__path__ + paths = ( + [main_plugin.__path__] + if isinstance(main_plugin.__path__, basestring) + else main_plugin.__path__ + ) # reverse plugin path order, so that custom plugins have a chance to # override the builtin plugins (from /rezplugins). @@ -126,7 +126,7 @@ def load_plugins(self): print_debug("searching plugin path %s...", path) for loader, modname, ispkg in pkgutil.iter_modules( - [path], package.__name__ + '.'): + [path], main_plugin.__name__ + '.'): if loader is None: continue @@ -265,8 +265,42 @@ def register_plugin(): 'rezplugins' is always found first. """ def __init__(self): + self._plugin_types_root = None self._plugin_types = {} + def locate_main_rezplugins(self): + if config.debug("plugins"): + print_debug("searching main 'rezplugins' module..") + + for imp in pkgutil.iter_importers(): + for name, ispkg in pkgutil.iter_importer_modules(imp): + if name != "rezplugins": + continue + + module_path = os.path.join(imp.path, "rezplugins") + verification = os.path.join(module_path, "MAIN") + if not os.path.isfile(verification): + if config.debug("plugins"): + print_debug("skipping external rezplugins module: %s" + % module_path) + continue + + self._plugin_types_root = module_path + return module_path + + raise RezPluginError("Main 'rezplugins' module not found, enable " + "REZ_DEBUG_PLUGINS for more detail.") + + def load_main_plugin_module(self, plugin_type): + root = self._plugin_types_root or self.locate_main_rezplugins() + for importer, modname, _ in pkgutil.iter_modules([root]): + if modname == plugin_type: + loader = importer.find_module(modname) + return loader.load_module(modname) + + raise RezPluginError("Plugin type '%s' not exists in main " + "'rezplugins' module: %s" % (plugin_type, root)) + # -- plugin types def _get_plugin_type(self, plugin_type): diff --git a/src/rezplugins/MAIN b/src/rezplugins/MAIN new file mode 100644 index 000000000..e29613198 --- /dev/null +++ b/src/rezplugins/MAIN @@ -0,0 +1,3 @@ +IMPORTANT: +This file indicates that this is where the main 'rezplugins' module is and +extend plugins from. From 72aba0d9b0d4f570c894fb11e39828b82039e52c Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 00:05:51 +0800 Subject: [PATCH 14/29] fix #960 - add prefix --- src/rez/plugin_managers.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 4b890e499..2e275e719 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -293,10 +293,11 @@ def locate_main_rezplugins(self): def load_main_plugin_module(self, plugin_type): root = self._plugin_types_root or self.locate_main_rezplugins() - for importer, modname, _ in pkgutil.iter_modules([root]): - if modname == plugin_type: - loader = importer.find_module(modname) - return loader.load_module(modname) + prefix = "rezplugins." + for importer, name, _ in pkgutil.iter_modules([root], prefix=prefix): + if name == prefix + plugin_type: + loader = importer.find_module(name) + return loader.load_module(name) raise RezPluginError("Plugin type '%s' not exists in main " "'rezplugins' module: %s" % (plugin_type, root)) From e8edcd5f82b03d6c2ce54f05c77d8d2a68296dea Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 13:15:58 +0800 Subject: [PATCH 15/29] revert fix #960 for future PR --- setup.py | 1 - src/rez/plugin_managers.py | 49 ++++++-------------------------------- src/rezplugins/MAIN | 3 --- 3 files changed, 7 insertions(+), 46 deletions(-) delete mode 100644 src/rezplugins/MAIN diff --git a/setup.py b/setup.py index 5792289fc..0522bd4ee 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,6 @@ def find_files(pattern, path=None, root="rez"): find_files('*', 'tests/data') + find_files('*.exe', 'vendor/distlib'), 'rezplugins': - ['MAIN'] + find_files('rezconfig', root='rezplugins') + find_files('*.cmake', 'build_system', root='rezplugins') + find_files('*', 'build_system/template_files', root='rezplugins'), diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 2e275e719..64d9ab95e 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -104,18 +104,18 @@ def register_plugin(self, plugin_name, plugin_class, plugin_module): self.plugin_modules[plugin_name] = plugin_module def load_plugins(self): - main_plugin = plugin_manager.load_main_plugin_module(self.type_name) + import pkgutil + from rez.backport.importlib import import_module + type_module_name = 'rezplugins.' + self.type_name + package = import_module(type_module_name) # on import, the `__path__` variable of the imported package is extended # to include existing directories on the plugin search path (via # extend_path, above). this means that `walk_packages` will walk over all # modules on the search path at the same level (.e.g in a # 'rezplugins/type_name' sub-directory). - paths = ( - [main_plugin.__path__] - if isinstance(main_plugin.__path__, basestring) - else main_plugin.__path__ - ) + paths = [package.__path__] if isinstance(package.__path__, basestring) \ + else package.__path__ # reverse plugin path order, so that custom plugins have a chance to # override the builtin plugins (from /rezplugins). @@ -126,7 +126,7 @@ def load_plugins(self): print_debug("searching plugin path %s...", path) for loader, modname, ispkg in pkgutil.iter_modules( - [path], main_plugin.__name__ + '.'): + [path], package.__name__ + '.'): if loader is None: continue @@ -265,43 +265,8 @@ def register_plugin(): 'rezplugins' is always found first. """ def __init__(self): - self._plugin_types_root = None self._plugin_types = {} - def locate_main_rezplugins(self): - if config.debug("plugins"): - print_debug("searching main 'rezplugins' module..") - - for imp in pkgutil.iter_importers(): - for name, ispkg in pkgutil.iter_importer_modules(imp): - if name != "rezplugins": - continue - - module_path = os.path.join(imp.path, "rezplugins") - verification = os.path.join(module_path, "MAIN") - if not os.path.isfile(verification): - if config.debug("plugins"): - print_debug("skipping external rezplugins module: %s" - % module_path) - continue - - self._plugin_types_root = module_path - return module_path - - raise RezPluginError("Main 'rezplugins' module not found, enable " - "REZ_DEBUG_PLUGINS for more detail.") - - def load_main_plugin_module(self, plugin_type): - root = self._plugin_types_root or self.locate_main_rezplugins() - prefix = "rezplugins." - for importer, name, _ in pkgutil.iter_modules([root], prefix=prefix): - if name == prefix + plugin_type: - loader = importer.find_module(name) - return loader.load_module(name) - - raise RezPluginError("Plugin type '%s' not exists in main " - "'rezplugins' module: %s" % (plugin_type, root)) - # -- plugin types def _get_plugin_type(self, plugin_type): diff --git a/src/rezplugins/MAIN b/src/rezplugins/MAIN deleted file mode 100644 index e29613198..000000000 --- a/src/rezplugins/MAIN +++ /dev/null @@ -1,3 +0,0 @@ -IMPORTANT: -This file indicates that this is where the main 'rezplugins' module is and -extend plugins from. From f5ad0feb48cd4e4ab54e17883b9579f87e7fe5ca Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 14:38:42 +0800 Subject: [PATCH 16/29] rename new plugin type to 'command' --- src/rez/cli/_util.py | 12 ++++++------ src/rez/{extension.py => command.py} | 12 ++++++------ src/rez/plugin_managers.py | 6 +++--- src/rez/tests/test_completion.py | 2 +- src/rezplugins/{extension => command}/__init__.py | 0 5 files changed, 16 insertions(+), 16 deletions(-) rename src/rez/{extension.py => command.py} (82%) rename src/rezplugins/{extension => command}/__init__.py (100%) diff --git a/src/rez/cli/_util.py b/src/rez/cli/_util.py index b8dc989e5..4132105ea 100644 --- a/src/rez/cli/_util.py +++ b/src/rez/cli/_util.py @@ -61,12 +61,12 @@ def load_plugin_cmd(): - """Load extension plugin subcommand + """Load subcommand from command type plugin - The extension plugin module must have attribute `command_behavior`, and - the value must be a dict. For example: + The command type plugin module should have attribute `command_behavior`, + and the value must be a dict if provided. For example: - # in your extension plugin module + # in your command plugin module command_behavior = { "hidden": False, # optional: bool "arg_mode": None, # optional: None, "passthrough", "grouped" @@ -79,8 +79,8 @@ def load_plugin_cmd(): ext_plugins = dict() - for plugin_name in plugin_manager.get_plugins("extension"): - module = plugin_manager.get_plugin_module("extension", plugin_name) + for plugin_name in plugin_manager.get_plugins("command"): + module = plugin_manager.get_plugin_module("command", plugin_name) if hasattr(module, "command_behavior"): try: diff --git a/src/rez/extension.py b/src/rez/command.py similarity index 82% rename from src/rez/extension.py rename to src/rez/command.py index deb7941de..ae40b4671 100644 --- a/src/rez/extension.py +++ b/src/rez/command.py @@ -2,22 +2,22 @@ from rez.config import config -class Extension(object): +class Command(object): """An interface for registering custom Rez subcommand To register plugin and expose subcommand, the plugin module.. * MUST have a module-level docstring (used as the command help) - * MUST have a module-level attribute `command_behavior` * MUST provide a `setup_parser()` function * MUST provide a `command()` function * MUST provide a `register_plugin()` function + * SHOULD have a module-level attribute `command_behavior` For example, a plugin named 'foo' and this is the `foo.py`: '''The docstring for command help, this is required. ''' - from rez.extension import Extension + from rez.extension import Command command_behavior = { "hidden": False, # optional: bool @@ -31,7 +31,7 @@ def command(opts, parser=None, extra_arg_groups=None): if opts.hello: print("world") - class ExtensionFoo(Extension): + class CommandFoo(Command): schema_dict = {} @classmethod @@ -39,7 +39,7 @@ def name(cls): return "foo" def register_plugin(): - return ExtensionFoo + return CommandFoo """ def __init__(self): @@ -48,5 +48,5 @@ def __init__(self): @classmethod def name(cls): - """Return the name of the Extension and rez-subcommand.""" + """Return the name of the Command and rez-subcommand.""" raise NotImplementedError diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 64d9ab95e..c30287994 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -386,10 +386,10 @@ class BuildProcessPluginType(RezPluginType): type_name = "build_process" -class ExtensionPluginType(RezPluginType): +class CommandPluginType(RezPluginType): """Support for different custom Rez applications/subcommands. """ - type_name = "extension" + type_name = "command" plugin_manager = RezPluginManager() @@ -401,7 +401,7 @@ class ExtensionPluginType(RezPluginType): plugin_manager.register_plugin_type(BuildSystemPluginType) plugin_manager.register_plugin_type(PackageRepositoryPluginType) plugin_manager.register_plugin_type(BuildProcessPluginType) -plugin_manager.register_plugin_type(ExtensionPluginType) +plugin_manager.register_plugin_type(CommandPluginType) # Copyright 2013-2016 Allan Johns. diff --git a/src/rez/tests/test_completion.py b/src/rez/tests/test_completion.py index 02bc24519..636aab06c 100644 --- a/src/rez/tests/test_completion.py +++ b/src/rez/tests/test_completion.py @@ -31,7 +31,7 @@ def _eq(prefix, expected_completions): _eq("plugin", ["plugins", "plugin_path"]) _eq("plugins", ["plugins", - "plugins.extension", + "plugins.command", "plugins.package_repository", "plugins.build_process", "plugins.build_system", diff --git a/src/rezplugins/extension/__init__.py b/src/rezplugins/command/__init__.py similarity index 100% rename from src/rezplugins/extension/__init__.py rename to src/rezplugins/command/__init__.py From 8d0af73a9977d394897711c036d055affff7009f Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 14:46:22 +0800 Subject: [PATCH 17/29] make 'command_behavior' as optional --- src/rez/cli/_util.py | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/rez/cli/_util.py b/src/rez/cli/_util.py index 4132105ea..6318e5230 100644 --- a/src/rez/cli/_util.py +++ b/src/rez/cli/_util.py @@ -68,10 +68,12 @@ def load_plugin_cmd(): # in your command plugin module command_behavior = { - "hidden": False, # optional: bool - "arg_mode": None, # optional: None, "passthrough", "grouped" + "hidden": False, # (bool): default False + "arg_mode": None, # (str): "passthrough", "grouped", default None } + If the attribute not present, default behavior will be given. + """ from rez.config import config from rez.utils.logging_ import print_debug @@ -82,23 +84,26 @@ def load_plugin_cmd(): for plugin_name in plugin_manager.get_plugins("command"): module = plugin_manager.get_plugin_module("command", plugin_name) - if hasattr(module, "command_behavior"): - try: - data = module.command_behavior.copy() - data.update({"module_name": module.__name__}) - ext_plugins[plugin_name] = data - - except Exception: - if config.debug("plugins"): - import traceback - from rez.vendor.six.six import StringIO - out = StringIO() - traceback.print_exc(file=out) - print_debug(out.getvalue()) - - elif config.debug("plugins"): - print_debug("Attribute 'command_behavior' not found in plugin " - "module %s, command not registered." % module.__name__) + behavior = getattr(module, "command_behavior", None) + if behavior is None: + behavior = dict() + + if config.debug("plugins"): + print_debug("Attribute 'command_behavior' not found in plugin " + "module %s, registering with default behavior." + % module.__name__) + try: + data = behavior.copy() + data.update({"module_name": module.__name__}) + ext_plugins[plugin_name] = data + + except Exception: + if config.debug("plugins"): + import traceback + from rez.vendor.six.six import StringIO + out = StringIO() + traceback.print_exc(file=out) + print_debug(out.getvalue()) return ext_plugins From f1e15eab72008217bcb81cccae23cb4a7002c765 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 16:56:33 +0800 Subject: [PATCH 18/29] cosmetic --- src/rez/plugin_managers.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index c30287994..a1b81f3d7 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -67,9 +67,9 @@ def append_if_valid(dir_): for dir_ in config.plugin_path: append_if_valid(dir_) # Extend new-style plugins - for loader, name, ispkg in pkgutil.iter_modules(): + for importer, name, ispkg in pkgutil.iter_modules(): if ispkg: - append_if_valid(os.path.join(loader.path, name)) + append_if_valid(os.path.join(importer.path, name)) return path @@ -125,10 +125,10 @@ def load_plugins(self): if config.debug("plugins"): print_debug("searching plugin path %s...", path) - for loader, modname, ispkg in pkgutil.iter_modules( + for importer, modname, ispkg in pkgutil.iter_modules( [path], package.__name__ + '.'): - if loader is None: + if importer is None: continue plugin_name = modname.split('.')[-1] @@ -141,19 +141,24 @@ def load_plugins(self): try: # load_module will force reload the module if it's # already loaded, so check for that - module = sys.modules.get(modname) - if module is None: - module = loader.find_module(modname).load_module(modname) - if hasattr(module, 'register_plugin') and \ - hasattr(module.register_plugin, '__call__'): - plugin_class = module.register_plugin() - if plugin_class != None: - self.register_plugin(plugin_name, plugin_class, module) + plugin_module = sys.modules.get(modname) + if plugin_module is None: + loader = importer.find_module(modname) + plugin_module = loader.load_module(modname) + + if (hasattr(plugin_module, "register_plugin") + and callable(plugin_module.register_plugin)): + + plugin_class = plugin_module.register_plugin() + if plugin_class is not None: + self.register_plugin(plugin_name, + plugin_class, + plugin_module) else: if config.debug("plugins"): print_warning( - "'register_plugin' function at %s: %s did not return a class." - % (path, modname)) + "'register_plugin' function at %s: %s did " + "not return a class." % (path, modname)) else: if config.debug("plugins"): print_warning( From 8e820b647b2a6700e12aae76727e735bb545b44e Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 17:51:09 +0800 Subject: [PATCH 19/29] add warnings --- src/rez/plugin_managers.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index a1b81f3d7..44ec465e5 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -118,7 +118,7 @@ def load_plugins(self): else package.__path__ # reverse plugin path order, so that custom plugins have a chance to - # override the builtin plugins (from /rezplugins). + # be found before the builtin plugins (from /rezplugins). paths = reversed(paths) for path in paths: @@ -135,10 +135,21 @@ def load_plugins(self): if plugin_name.startswith('_') or plugin_name == 'rezconfig': continue + if plugin_name in self.plugin_modules: + # same named plugins will have identical module name, + # which will just reuse previous imported module from + # `sys.modules` below. skipping the rest of the process + # for good. + if config.debug("plugins"): + print_warning("skipped same named %s plugin at %s: %s" + % (self.type_name, path, modname)) + continue + if config.debug("plugins"): print_debug("loading %s plugin at %s: %s..." % (self.type_name, path, modname)) try: + # nerdvegas/rez#218 # load_module will force reload the module if it's # already loaded, so check for that plugin_module = sys.modules.get(modname) @@ -146,6 +157,14 @@ def load_plugins(self): loader = importer.find_module(modname) plugin_module = loader.load_module(modname) + elif os.path.dirname(plugin_module.__file__) != path: + if config.debug("plugins"): + # this should not happen but if it does, tell why. + print_warning( + "plugin module %s is not loaded from current " + "load path but reused from previous imported " + "path: %s" % (modname, plugin_module.__file__)) + if (hasattr(plugin_module, "register_plugin") and callable(plugin_module.register_plugin)): From 2b9cd2be3f787a7f1ab638fd2487ae2317ce7304 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 17:59:31 +0800 Subject: [PATCH 20/29] avoid sys modules re-scan --- src/rez/plugin_managers.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 44ec465e5..20ce8a873 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -67,9 +67,8 @@ def append_if_valid(dir_): for dir_ in config.plugin_path: append_if_valid(dir_) # Extend new-style plugins - for importer, name, ispkg in pkgutil.iter_modules(): - if ispkg: - append_if_valid(os.path.join(importer.path, name)) + for dir_ in plugin_manager.sys_module_paths: + append_if_valid(dir_) return path @@ -291,6 +290,14 @@ def register_plugin(): def __init__(self): self._plugin_types = {} + @cached_property + def sys_module_paths(self): + paths = [] + for importer, name, ispkg in pkgutil.iter_modules(): + if ispkg: + paths.append(os.path.join(importer.path, name)) + return paths + # -- plugin types def _get_plugin_type(self, plugin_type): From 19107ef3705c79b5cb0dc94b01815ad6c3d95527 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 19:26:23 +0800 Subject: [PATCH 21/29] missed rename --- src/rez/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rez/command.py b/src/rez/command.py index ae40b4671..aad26a9ff 100644 --- a/src/rez/command.py +++ b/src/rez/command.py @@ -17,7 +17,7 @@ class Command(object): '''The docstring for command help, this is required. ''' - from rez.extension import Command + from rez.command import Command command_behavior = { "hidden": False, # optional: bool From e1739e93865263924e170c81bfb60a9260c1a62c Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 21:58:14 +0800 Subject: [PATCH 22/29] add plugin_manager tests --- src/rez/plugin_managers.py | 5 + src/rez/tests/data/extensions/bar/__init__.py | 0 .../extensions/bar/rezplugins/__init__.py | 0 .../rezplugins/package_repository/__init__.py | 0 .../rezplugins/package_repository/memory.py | 13 ++ src/rez/tests/data/extensions/foo/__init__.py | 0 .../extensions/foo/rezplugins/__init__.py | 0 .../rezplugins/package_repository/__init__.py | 0 .../rezplugins/package_repository/cloud.py | 12 ++ .../extensions/non-mod/rezplugins/__init__.py | 0 .../rezplugins/package_repository/__init__.py | 0 .../rezplugins/package_repository/memory.py | 13 ++ src/rez/tests/test_plugin_manager.py | 112 ++++++++++++++++++ 13 files changed, 155 insertions(+) create mode 100644 src/rez/tests/data/extensions/bar/__init__.py create mode 100644 src/rez/tests/data/extensions/bar/rezplugins/__init__.py create mode 100644 src/rez/tests/data/extensions/bar/rezplugins/package_repository/__init__.py create mode 100644 src/rez/tests/data/extensions/bar/rezplugins/package_repository/memory.py create mode 100644 src/rez/tests/data/extensions/foo/__init__.py create mode 100644 src/rez/tests/data/extensions/foo/rezplugins/__init__.py create mode 100644 src/rez/tests/data/extensions/foo/rezplugins/package_repository/__init__.py create mode 100644 src/rez/tests/data/extensions/foo/rezplugins/package_repository/cloud.py create mode 100644 src/rez/tests/data/extensions/non-mod/rezplugins/__init__.py create mode 100644 src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/__init__.py create mode 100644 src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/memory.py create mode 100644 src/rez/tests/test_plugin_manager.py diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index 20ce8a873..dbcab4eb8 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -73,6 +73,11 @@ def append_if_valid(dir_): return path +def uncache_sys_module_paths(instance=None): + instance = instance or plugin_manager + cached_property.uncache(instance, "sys_module_paths") + + class RezPluginType(object): """An abstract base class representing a single type of plugin. diff --git a/src/rez/tests/data/extensions/bar/__init__.py b/src/rez/tests/data/extensions/bar/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/bar/rezplugins/__init__.py b/src/rez/tests/data/extensions/bar/rezplugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/bar/rezplugins/package_repository/__init__.py b/src/rez/tests/data/extensions/bar/rezplugins/package_repository/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/bar/rezplugins/package_repository/memory.py b/src/rez/tests/data/extensions/bar/rezplugins/package_repository/memory.py new file mode 100644 index 000000000..4a0a72437 --- /dev/null +++ b/src/rez/tests/data/extensions/bar/rezplugins/package_repository/memory.py @@ -0,0 +1,13 @@ + +from rez.package_repository import PackageRepository + + +class MemoryPackageRepository(PackageRepository): + @classmethod + def name(cls): + return "memory" + on_test = "bar" + + +def register_plugin(): + return MemoryPackageRepository diff --git a/src/rez/tests/data/extensions/foo/__init__.py b/src/rez/tests/data/extensions/foo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/foo/rezplugins/__init__.py b/src/rez/tests/data/extensions/foo/rezplugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/foo/rezplugins/package_repository/__init__.py b/src/rez/tests/data/extensions/foo/rezplugins/package_repository/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/foo/rezplugins/package_repository/cloud.py b/src/rez/tests/data/extensions/foo/rezplugins/package_repository/cloud.py new file mode 100644 index 000000000..f5729a185 --- /dev/null +++ b/src/rez/tests/data/extensions/foo/rezplugins/package_repository/cloud.py @@ -0,0 +1,12 @@ + +from rez.package_repository import PackageRepository + + +class CloudPackageRepository(PackageRepository): + @classmethod + def name(cls): + return "cloud" + + +def register_plugin(): + return CloudPackageRepository diff --git a/src/rez/tests/data/extensions/non-mod/rezplugins/__init__.py b/src/rez/tests/data/extensions/non-mod/rezplugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/__init__.py b/src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/memory.py b/src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/memory.py new file mode 100644 index 000000000..263081007 --- /dev/null +++ b/src/rez/tests/data/extensions/non-mod/rezplugins/package_repository/memory.py @@ -0,0 +1,13 @@ + +from rez.package_repository import PackageRepository + + +class MemoryPackageRepository(PackageRepository): + @classmethod + def name(cls): + return "memory" + on_test = "non-mod" + + +def register_plugin(): + return MemoryPackageRepository diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py new file mode 100644 index 000000000..a5a73d99f --- /dev/null +++ b/src/rez/tests/test_plugin_manager.py @@ -0,0 +1,112 @@ +""" +test rezplugins manager behaviors +""" +from rez.tests.util import TestBase, TempdirMixin, restore_sys_path +from rez.plugin_managers import ( + uncache_sys_module_paths, + RezPluginManager, + ShellPluginType, + ReleaseVCSPluginType, + ReleaseHookPluginType, + BuildSystemPluginType, + PackageRepositoryPluginType, + BuildProcessPluginType, + CommandPluginType +) +import os +import sys +import unittest + + +class TestPluginManagers(TestBase, TempdirMixin): + def __init__(self, *nargs, **kwargs): + TestBase.__init__(self, *nargs, **kwargs) + self._original_modules = set() + self.plugin_manager = None + + def setUp(self): + TestBase.setUp(self) + uncache_sys_module_paths() + + plugin_manager = RezPluginManager() + plugin_manager.register_plugin_type(ShellPluginType) + plugin_manager.register_plugin_type(ReleaseVCSPluginType) + plugin_manager.register_plugin_type(ReleaseHookPluginType) + plugin_manager.register_plugin_type(BuildSystemPluginType) + plugin_manager.register_plugin_type(PackageRepositoryPluginType) + plugin_manager.register_plugin_type(BuildProcessPluginType) + plugin_manager.register_plugin_type(CommandPluginType) + + self._original_modules.update(sys.modules.keys()) + self.plugin_manager = plugin_manager + + def tearDown(self): + TestBase.tearDown(self) + self.plugin_manager = None + + for key in set(sys.modules.keys()): + if key not in self._original_modules: + del sys.modules[key] + self._original_modules.clear() + + def test_old_loading_style(self): + """Test loading rez plugin from plugin_path""" + path = os.path.realpath(os.path.dirname(__file__)) + self.update_settings(dict( + plugin_path=[os.path.join(path, "data", "extensions", "foo")] + )) + + cloud_cls = self.plugin_manager.get_plugin_class( + "package_repository", "cloud") + self.assertEqual(cloud_cls.name(), "cloud") + + def test_new_loading_style(self): + """Test loading rez plugin from python modules""" + path = os.path.realpath(os.path.dirname(__file__)) + with restore_sys_path(): + sys.path.append(os.path.join(path, "data", "extensions")) + + cloud_cls = self.plugin_manager.get_plugin_class( + "package_repository", "cloud") + self.assertEqual(cloud_cls.name(), "cloud") + + def test_plugin_override_1(self): + """Test plugin from plugin_path can override the default""" + path = os.path.realpath(os.path.dirname(__file__)) + self.update_settings(dict( + plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] + )) + + mem_cls = self.plugin_manager.get_plugin_class( + "package_repository", "memory") + self.assertEqual("non-mod", mem_cls.on_test) + + def test_plugin_override_2(self): + """Test plugin from python modules can override the default""" + path = os.path.realpath(os.path.dirname(__file__)) + with restore_sys_path(): + sys.path.append(os.path.join(path, "data", "extensions")) + + mem_cls = self.plugin_manager.get_plugin_class( + "package_repository", "memory") + self.assertEqual("bar", mem_cls.on_test) + + def test_plugin_override_3(self): + """Test plugin from python modules can override plugin_path""" + path = os.path.realpath(os.path.dirname(__file__)) + with restore_sys_path(): + # setup new + sys.path.append(os.path.join(path, "data", "extensions")) + # setup old + self.update_settings(dict( + plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] + )) + + mem_cls = self.plugin_manager.get_plugin_class( + "package_repository", "memory") + self.assertEqual("bar", mem_cls.on_test) + + +if __name__ == '__main__': + unittest.main() + From 9e95326d56cfc137d8cf83a6db3b4b0a02844da7 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 23 Mar 2021 23:12:18 +0800 Subject: [PATCH 23/29] add example rez extension --- example_extensions/hello_cmd/README.md | 12 ++++++ example_extensions/hello_cmd/__init__.py | 0 example_extensions/hello_cmd/lib.py | 5 +++ .../hello_cmd/rezplugins/__init__.py | 0 .../hello_cmd/rezplugins/command/__init__.py | 0 .../hello_cmd/rezplugins/command/rezconfig.py | 4 ++ .../hello_cmd/rezplugins/command/world.py | 37 +++++++++++++++++++ example_extensions/hello_cmd/setup.py | 12 ++++++ 8 files changed, 70 insertions(+) create mode 100644 example_extensions/hello_cmd/README.md create mode 100644 example_extensions/hello_cmd/__init__.py create mode 100644 example_extensions/hello_cmd/lib.py create mode 100644 example_extensions/hello_cmd/rezplugins/__init__.py create mode 100644 example_extensions/hello_cmd/rezplugins/command/__init__.py create mode 100644 example_extensions/hello_cmd/rezplugins/command/rezconfig.py create mode 100644 example_extensions/hello_cmd/rezplugins/command/world.py create mode 100644 example_extensions/hello_cmd/setup.py diff --git a/example_extensions/hello_cmd/README.md b/example_extensions/hello_cmd/README.md new file mode 100644 index 000000000..dc4d6c6a3 --- /dev/null +++ b/example_extensions/hello_cmd/README.md @@ -0,0 +1,12 @@ +Welcome to install your first rez extension! + +Currently, please pip install this Python package into Rez's venv, and run +``` +$ rez -i +``` +You should see a plugin named "world" in the plugin list. +And now you could do +``` +$ rez world -h +``` +to see what you could do about it. diff --git a/example_extensions/hello_cmd/__init__.py b/example_extensions/hello_cmd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/example_extensions/hello_cmd/lib.py b/example_extensions/hello_cmd/lib.py new file mode 100644 index 000000000..b4f2224c4 --- /dev/null +++ b/example_extensions/hello_cmd/lib.py @@ -0,0 +1,5 @@ + +def get_message_from_world(): + from rez.config import config + message = config.plugins.command.world.message + return message diff --git a/example_extensions/hello_cmd/rezplugins/__init__.py b/example_extensions/hello_cmd/rezplugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/example_extensions/hello_cmd/rezplugins/command/__init__.py b/example_extensions/hello_cmd/rezplugins/command/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/example_extensions/hello_cmd/rezplugins/command/rezconfig.py b/example_extensions/hello_cmd/rezplugins/command/rezconfig.py new file mode 100644 index 000000000..3f3e82a6a --- /dev/null +++ b/example_extensions/hello_cmd/rezplugins/command/rezconfig.py @@ -0,0 +1,4 @@ + +world = { + "message": "welcome to this world." +} diff --git a/example_extensions/hello_cmd/rezplugins/command/world.py b/example_extensions/hello_cmd/rezplugins/command/world.py new file mode 100644 index 000000000..d481c8da6 --- /dev/null +++ b/example_extensions/hello_cmd/rezplugins/command/world.py @@ -0,0 +1,37 @@ +""" +Demoing Rez's command type plugin +""" +from rez.command import Command + +# This attribute is optional, default behavior will be applied if not present. +command_behavior = { + "hidden": False, # (bool): default False + "arg_mode": None, # (str): "passthrough", "grouped", default None +} + + +def setup_parser(parser, completions=False): + parser.add_argument("-m", "--message", action="store_true", + help="Print message from world.") + + +def command(opts, parser=None, extra_arg_groups=None): + from hello_cmd import lib + + if opts.message: + msg = lib.get_message_from_world() + print(msg) + return + + print("Please use '-h' flag to see what you can do to this world !") + + +class WorldCommand(Command): + + @classmethod + def name(cls): + return "world" + + +def register_plugin(): + return WorldCommand diff --git a/example_extensions/hello_cmd/setup.py b/example_extensions/hello_cmd/setup.py new file mode 100644 index 000000000..827638f41 --- /dev/null +++ b/example_extensions/hello_cmd/setup.py @@ -0,0 +1,12 @@ +from __future__ import print_function, with_statement +from setuptools import setup + + +setup( + name="hello_cmd", + version="1.0", + package_dir={"hello_cmd": ""}, + packages=["hello_cmd", + "hello_cmd.rezplugins", + "hello_cmd.rezplugins.command"], +) From a184fa62fe30c893a330ba537026ad8dfa447295 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 24 Mar 2021 01:55:42 +0800 Subject: [PATCH 24/29] fix test --- src/rez/tests/test_plugin_manager.py | 66 ++++++++++++---------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index a5a73d99f..b9b96a5f3 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -2,17 +2,7 @@ test rezplugins manager behaviors """ from rez.tests.util import TestBase, TempdirMixin, restore_sys_path -from rez.plugin_managers import ( - uncache_sys_module_paths, - RezPluginManager, - ShellPluginType, - ReleaseVCSPluginType, - ReleaseHookPluginType, - BuildSystemPluginType, - PackageRepositoryPluginType, - BuildProcessPluginType, - CommandPluginType -) +from rez.plugin_managers import plugin_manager, uncache_sys_module_paths import os import sys import unittest @@ -21,33 +11,35 @@ class TestPluginManagers(TestBase, TempdirMixin): def __init__(self, *nargs, **kwargs): TestBase.__init__(self, *nargs, **kwargs) - self._original_modules = set() - self.plugin_manager = None + self._reset_plugin_manager() - def setUp(self): - TestBase.setUp(self) + @classmethod + def _reset_plugin_manager(cls): uncache_sys_module_paths() - plugin_manager = RezPluginManager() - plugin_manager.register_plugin_type(ShellPluginType) - plugin_manager.register_plugin_type(ReleaseVCSPluginType) - plugin_manager.register_plugin_type(ReleaseHookPluginType) - plugin_manager.register_plugin_type(BuildSystemPluginType) - plugin_manager.register_plugin_type(PackageRepositoryPluginType) - plugin_manager.register_plugin_type(BuildProcessPluginType) - plugin_manager.register_plugin_type(CommandPluginType) + plugin_types = [] + for singleton in plugin_manager._plugin_types.values(): + plugin_types.append(singleton.instance_class) + plugin_manager._plugin_types.clear() - self._original_modules.update(sys.modules.keys()) - self.plugin_manager = plugin_manager + for plugin_type in plugin_types: + plugin_manager.register_plugin_type(plugin_type) - def tearDown(self): - TestBase.tearDown(self) - self.plugin_manager = None - - for key in set(sys.modules.keys()): - if key not in self._original_modules: + for key in list(sys.modules.keys()): + if key.startswith("rezplugins."): del sys.modules[key] - self._original_modules.clear() + + @classmethod + def setUpClass(cls): + cls.settings = {"debug_plugins": True} + + @classmethod + def tearDownClass(cls): + cls._reset_plugin_manager() + + def setUp(self): + TestBase.setUp(self) + self._reset_plugin_manager() def test_old_loading_style(self): """Test loading rez plugin from plugin_path""" @@ -56,7 +48,7 @@ def test_old_loading_style(self): plugin_path=[os.path.join(path, "data", "extensions", "foo")] )) - cloud_cls = self.plugin_manager.get_plugin_class( + cloud_cls = plugin_manager.get_plugin_class( "package_repository", "cloud") self.assertEqual(cloud_cls.name(), "cloud") @@ -66,7 +58,7 @@ def test_new_loading_style(self): with restore_sys_path(): sys.path.append(os.path.join(path, "data", "extensions")) - cloud_cls = self.plugin_manager.get_plugin_class( + cloud_cls = plugin_manager.get_plugin_class( "package_repository", "cloud") self.assertEqual(cloud_cls.name(), "cloud") @@ -77,7 +69,7 @@ def test_plugin_override_1(self): plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] )) - mem_cls = self.plugin_manager.get_plugin_class( + mem_cls = plugin_manager.get_plugin_class( "package_repository", "memory") self.assertEqual("non-mod", mem_cls.on_test) @@ -87,7 +79,7 @@ def test_plugin_override_2(self): with restore_sys_path(): sys.path.append(os.path.join(path, "data", "extensions")) - mem_cls = self.plugin_manager.get_plugin_class( + mem_cls = plugin_manager.get_plugin_class( "package_repository", "memory") self.assertEqual("bar", mem_cls.on_test) @@ -102,7 +94,7 @@ def test_plugin_override_3(self): plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] )) - mem_cls = self.plugin_manager.get_plugin_class( + mem_cls = plugin_manager.get_plugin_class( "package_repository", "memory") self.assertEqual("bar", mem_cls.on_test) From 9ddc4127b6cde39c153b33d9ac8b1168e1a35492 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 24 Mar 2021 03:01:16 +0800 Subject: [PATCH 25/29] fix test with proper package_repository reset --- src/rez/tests/test_plugin_manager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index b9b96a5f3..a449d3e5c 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -3,6 +3,7 @@ """ from rez.tests.util import TestBase, TempdirMixin, restore_sys_path from rez.plugin_managers import plugin_manager, uncache_sys_module_paths +from rez.package_repository import package_repository_manager import os import sys import unittest @@ -15,6 +16,10 @@ def __init__(self, *nargs, **kwargs): @classmethod def _reset_plugin_manager(cls): + # for resetting package_repository type plugins + package_repository_manager.clear_caches() + package_repository_manager.pool.resource_classes.clear() + # for resetting new-style plugins uncache_sys_module_paths() plugin_types = [] From c624f0e5099669e863592d2261108cf29e5fbaf2 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 30 Mar 2021 12:21:35 +0800 Subject: [PATCH 26/29] update test --- src/rez/tests/test_plugin_manager.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index a449d3e5c..f8951ff7c 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -4,7 +4,6 @@ from rez.tests.util import TestBase, TempdirMixin, restore_sys_path from rez.plugin_managers import plugin_manager, uncache_sys_module_paths from rez.package_repository import package_repository_manager -import os import sys import unittest @@ -48,9 +47,8 @@ def setUp(self): def test_old_loading_style(self): """Test loading rez plugin from plugin_path""" - path = os.path.realpath(os.path.dirname(__file__)) self.update_settings(dict( - plugin_path=[os.path.join(path, "data", "extensions", "foo")] + plugin_path=[self.data_path("extensions", "foo")] )) cloud_cls = plugin_manager.get_plugin_class( @@ -59,9 +57,8 @@ def test_old_loading_style(self): def test_new_loading_style(self): """Test loading rez plugin from python modules""" - path = os.path.realpath(os.path.dirname(__file__)) with restore_sys_path(): - sys.path.append(os.path.join(path, "data", "extensions")) + sys.path.append(self.data_path("data", "extensions")) cloud_cls = plugin_manager.get_plugin_class( "package_repository", "cloud") @@ -69,9 +66,8 @@ def test_new_loading_style(self): def test_plugin_override_1(self): """Test plugin from plugin_path can override the default""" - path = os.path.realpath(os.path.dirname(__file__)) self.update_settings(dict( - plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] + plugin_path=[self.data_path("extensions", "non-mod")] )) mem_cls = plugin_manager.get_plugin_class( @@ -80,9 +76,8 @@ def test_plugin_override_1(self): def test_plugin_override_2(self): """Test plugin from python modules can override the default""" - path = os.path.realpath(os.path.dirname(__file__)) with restore_sys_path(): - sys.path.append(os.path.join(path, "data", "extensions")) + sys.path.append(self.data_path("extensions")) mem_cls = plugin_manager.get_plugin_class( "package_repository", "memory") @@ -90,13 +85,12 @@ def test_plugin_override_2(self): def test_plugin_override_3(self): """Test plugin from python modules can override plugin_path""" - path = os.path.realpath(os.path.dirname(__file__)) with restore_sys_path(): # setup new - sys.path.append(os.path.join(path, "data", "extensions")) + sys.path.append(self.data_path("extensions")) # setup old self.update_settings(dict( - plugin_path=[os.path.join(path, "data", "extensions", "non-mod")] + plugin_path=[self.data_path("extensions", "non-mod")] )) mem_cls = plugin_manager.get_plugin_class( From 1368513a839562c168d8d35c43838abda217b876 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 30 Mar 2021 12:33:39 +0800 Subject: [PATCH 27/29] fix test data path --- src/rez/tests/test_plugin_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index f8951ff7c..19e5c6db4 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -58,7 +58,7 @@ def test_old_loading_style(self): def test_new_loading_style(self): """Test loading rez plugin from python modules""" with restore_sys_path(): - sys.path.append(self.data_path("data", "extensions")) + sys.path.append(self.data_path("extensions")) cloud_cls = plugin_manager.get_plugin_class( "package_repository", "cloud") From ae6ba36ad83563a3039220e415235610fd322e0d Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 14 Apr 2021 19:43:05 +0800 Subject: [PATCH 28/29] fix PEP8 W391 --- src/rez/tests/test_plugin_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index 19e5c6db4..f7717ca4c 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -100,4 +100,3 @@ def test_plugin_override_3(self): if __name__ == '__main__': unittest.main() - From 6aebd7273f697c238eb2c157e66d1e528f1b44ea Mon Sep 17 00:00:00 2001 From: David Lai Date: Mon, 7 Jun 2021 12:58:19 +0800 Subject: [PATCH 29/29] only include module that has `rezplugins` --- src/rez/plugin_managers.py | 17 +++++++++++------ src/rez/tests/test_plugin_manager.py | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/rez/plugin_managers.py b/src/rez/plugin_managers.py index dbcab4eb8..87a52de7f 100644 --- a/src/rez/plugin_managers.py +++ b/src/rez/plugin_managers.py @@ -67,15 +67,15 @@ def append_if_valid(dir_): for dir_ in config.plugin_path: append_if_valid(dir_) # Extend new-style plugins - for dir_ in plugin_manager.sys_module_paths: + for dir_ in plugin_manager.rezplugins_module_paths: append_if_valid(dir_) return path -def uncache_sys_module_paths(instance=None): +def uncache_rezplugins_module_paths(instance=None): instance = instance or plugin_manager - cached_property.uncache(instance, "sys_module_paths") + cached_property.uncache(instance, "rezplugins_module_paths") class RezPluginType(object): @@ -296,11 +296,16 @@ def __init__(self): self._plugin_types = {} @cached_property - def sys_module_paths(self): + def rezplugins_module_paths(self): paths = [] for importer, name, ispkg in pkgutil.iter_modules(): - if ispkg: - paths.append(os.path.join(importer.path, name)) + if not ispkg: + continue + + module_path = os.path.join(importer.path, name) + if os.path.isdir(os.path.join(module_path, "rezplugins")): + paths.append(module_path) + return paths # -- plugin types diff --git a/src/rez/tests/test_plugin_manager.py b/src/rez/tests/test_plugin_manager.py index f7717ca4c..9ae6d6ced 100644 --- a/src/rez/tests/test_plugin_manager.py +++ b/src/rez/tests/test_plugin_manager.py @@ -2,7 +2,7 @@ test rezplugins manager behaviors """ from rez.tests.util import TestBase, TempdirMixin, restore_sys_path -from rez.plugin_managers import plugin_manager, uncache_sys_module_paths +from rez.plugin_managers import plugin_manager, uncache_rezplugins_module_paths from rez.package_repository import package_repository_manager import sys import unittest @@ -19,7 +19,7 @@ def _reset_plugin_manager(cls): package_repository_manager.clear_caches() package_repository_manager.pool.resource_classes.clear() # for resetting new-style plugins - uncache_sys_module_paths() + uncache_rezplugins_module_paths() plugin_types = [] for singleton in plugin_manager._plugin_types.values():