Skip to content

Commit

Permalink
Move plugin disabling into argument parser initialization. (conda#12904)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ken Odegard <[email protected]>
Co-authored-by: Bianca Henderson <[email protected]>
  • Loading branch information
4 people authored Jul 19, 2023
1 parent 67a19b1 commit 60c7ba9
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ repos:
# auto sort Python imports
- id: isort
- repo: https://github.com/psf/black
rev: 23.3.0
rev: 23.7.0
hooks:
# auto format Python codes
- id: black
Expand Down
21 changes: 15 additions & 6 deletions conda/cli/conda_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,6 @@ def do_call(args: argparse.Namespace, parser: ArgumentParser):
Serves as the primary entry point for commands referred to in this file and for
all registered plugin subcommands.
"""
# disable all external plugins if requested
if context.no_plugins:
context.plugin_manager.disable_external_plugins()

# let's see if during the parsing phase it was discovered that the
# called command was in fact a plugin subcommand
plugin_subcommand = getattr(args, "plugin_subcommand", None)
Expand Down Expand Up @@ -176,10 +172,19 @@ def __init__(self, *args, **kwargs):
if add_custom_help:
add_parser_help(self)

# FUTURE: Python 3.8+, replace with functools.cached_property
@property
@lru_cache(maxsize=None)
def plugins_disabled(self) -> bool:
return "--no-plugins" in sys.argv or context.no_plugins

# FUTURE: Python 3.8+, replace with functools.cached_property
@property
@lru_cache(maxsize=None)
def plugin_subcommands(self):
# first, let's disable all external plugins if requested
if self.plugins_disabled:
context.plugin_manager.disable_external_plugins()
return {
subcommand.name: subcommand
for subcommand in context.plugin_manager.get_hook_results("subcommands")
Expand Down Expand Up @@ -243,10 +248,14 @@ def error(self, message):
def print_help(self):
super().print_help()

if sys.argv[1:] in ([], [""], ["help"], ["-h"], ["--help"]):
# using a set here to check for existence independent of order
if set(sys.argv[1:]).intersection({"", "help", "-h", "--help"}):
from .find_commands import find_commands

other_commands = set(find_commands()).difference(self.plugin_subcommands)
other_commands = find_commands()
if not self.plugins_disabled:
other_commands = set(other_commands).difference(self.plugin_subcommands)

if other_commands:
builder = [""]
builder.append("conda commands available from other packages (legacy):")
Expand Down
7 changes: 4 additions & 3 deletions conda/exception_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ def print_unexpected_error_report(self, error_report):
"",
"Example: conda --no-plugins install <package>",
"",
"Alternatively, you can set the 'no_plugins' option to run the command",
"without plugins enabled. You can do this by running:",
" $ conda config --set no_plugins true",
"Alternatively, you can set the CONDA_NO_PLUGINS environment variable on",
"the command line to run the command without plugins enabled.",
"",
"Example: CONDA_NO_PLUGINS=true conda install <package>",
"",
]
)
Expand Down
2 changes: 1 addition & 1 deletion conda/plugins/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def disable_external_plugins(self) -> None:
Disables all currently registered plugins except built-in conda plugins
"""
for name, plugin in self.list_name_plugin():
if not name.startswith("conda.plugins."):
if not name.startswith("conda.plugins.") and not self.is_blocked(name):
self.set_blocked(name)


Expand Down
5 changes: 2 additions & 3 deletions tests/plugins/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def conda_solvers(self):
)


def test_load_no_plugins(plugin_manager):
def test_load_without_plugins(plugin_manager):
plugin_names = plugin_manager.load_plugins()
assert plugin_names == 0

Expand Down Expand Up @@ -151,8 +151,7 @@ def test_get_canonical_name_instance(plugin_manager):
@pytest.mark.parametrize("plugin", [this_module, VerboseSolverPlugin])
def test_disable_external_plugins(plugin_manager, plugin: object):
"""
Run a test to ensure we can successfully disable externally registered plugins
with the --no-plugins flag
Run a test to ensure we can successfully disable externally registered plugins.
"""
assert plugin_manager.load_plugins(plugin) == 1
assert plugin_manager.get_plugins() == {plugin}
Expand Down
30 changes: 29 additions & 1 deletion tests/plugins/test_subcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from conda import plugins
from conda.auxlib.ish import dals
from conda.cli.conda_argparse import BUILTIN_COMMANDS
from conda.base.context import context
from conda.cli.conda_argparse import BUILTIN_COMMANDS, generate_parser
from conda.exceptions import CommandNotFoundError
from conda.plugins.types import CondaSubcommand
from conda.testing import CondaCLIFixture

Expand Down Expand Up @@ -104,3 +106,29 @@ def test_cannot_override_builtin_commands(command, plugin_manager, mocker, conda
]

assert mocked.mock_calls == []


def test_parser_no_plugins(plugin_manager):
subcommand_plugin = SubcommandPlugin(name="custom", summary="Summary.")
assert plugin_manager.load_plugins(subcommand_plugin) == 1
assert plugin_manager.is_registered(subcommand_plugin)

parser = generate_parser()

with pytest.raises(CommandNotFoundError):
parser.parse_args(["foobar"])

args = parser.parse_args(["custom"])
assert args.cmd == "custom"

# setting no_plugins to make sure the
# plugin disabling happens when the
# argument parser is created
context.no_plugins = True
parser = generate_parser()

with pytest.raises(CommandNotFoundError):
parser.parse_args(["foobar"])

with pytest.raises(CommandNotFoundError):
args = parser.parse_args(["custom"])

0 comments on commit 60c7ba9

Please sign in to comment.