From 36ec2908f9fe0f9822aba048892dde1dcc8264db Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 17 Jul 2023 16:19:04 +0200 Subject: [PATCH] Convert conda-build entrypoints into plugins (#4921) --- conda_build/cli/main_build.py | 22 ++++----- conda_build/cli/main_convert.py | 7 ++- conda_build/cli/main_debug.py | 20 ++++---- conda_build/cli/main_develop.py | 7 ++- conda_build/cli/main_index.py | 4 +- conda_build/cli/main_inspect.py | 7 ++- conda_build/cli/main_metapackage.py | 11 ++--- conda_build/cli/main_render.py | 17 +++---- conda_build/cli/main_skeleton.py | 9 ++-- conda_build/plugin.py | 62 ++++++++++++++++++++++++ news/4921-convert-entrypoints-to-plugins | 20 ++++++++ pyproject.toml | 3 ++ recipe/meta.yaml | 2 +- 13 files changed, 142 insertions(+), 49 deletions(-) create mode 100644 conda_build/plugin.py create mode 100644 news/4921-convert-entrypoints-to-plugins diff --git a/conda_build/cli/main_build.py b/conda_build/cli/main_build.py index ca3bb8a3cf..999be96663 100644 --- a/conda_build/cli/main_build.py +++ b/conda_build/cli/main_build.py @@ -13,23 +13,18 @@ from conda.common.io import dashlist from glob2 import glob -import conda_build.api as api -import conda_build.build as build -import conda_build.source as source -import conda_build.utils as utils -from conda_build.cli.actions import KeyValueAction -from conda_build.cli.main_render import get_render_parser -from conda_build.conda_interface import ( - add_parser_channels, - binstar_upload, - cc_conda_build, -) -from conda_build.config import Config, get_channel_urls, zstd_compression_level_default -from conda_build.utils import LoggingContext +from .. import api, build, source, utils +from ..conda_interface import add_parser_channels, binstar_upload, cc_conda_build +from ..config import Config, get_channel_urls, zstd_compression_level_default +from ..deprecations import deprecated +from ..utils import LoggingContext +from .actions import KeyValueAction +from .main_render import get_render_parser def parse_args(args): p = get_render_parser() + p.prog = "conda build" p.description = dals( """ Tool for building conda packages. A conda package is a binary tarball @@ -588,6 +583,7 @@ def execute(args): return outputs +@deprecated("3.26.0", "4.0.0", addendum="Use `conda build` instead.") def main(): try: execute(sys.argv[1:]) diff --git a/conda_build/cli/main_convert.py b/conda_build/cli/main_convert.py index 43006a1e3b..4c09cfc1da 100644 --- a/conda_build/cli/main_convert.py +++ b/conda_build/cli/main_convert.py @@ -4,8 +4,9 @@ import sys from os.path import abspath, expanduser -from conda_build import api -from conda_build.conda_interface import ArgumentParser +from .. import api +from ..conda_interface import ArgumentParser +from ..deprecations import deprecated logging.basicConfig(level=logging.INFO) @@ -36,6 +37,7 @@ def parse_args(args): p = ArgumentParser( + prog="conda convert", description=""" Various tools to convert conda packages. Takes a pure Python package build for one platform and converts it to work on one or more other platforms, or @@ -127,5 +129,6 @@ def execute(args): api.convert(f, **args.__dict__) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda convert` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_debug.py b/conda_build/cli/main_debug.py index 702fbfc798..00c6eeb230 100644 --- a/conda_build/cli/main_debug.py +++ b/conda_build/cli/main_debug.py @@ -4,13 +4,11 @@ import sys from argparse import ArgumentParser -from conda_build import api -from conda_build.cli import validators as valid - -# we extend the render parser because we basically need to render the recipe before -# we can say what env to create. This is not really true for debugging tests, but meh... -from conda_build.cli.main_render import get_render_parser -from conda_build.utils import on_win +from .. import api +from ..deprecations import deprecated +from ..utils import on_win +from . import validators as valid +from .main_render import get_render_parser logging.basicConfig(level=logging.INFO) @@ -18,6 +16,7 @@ def get_parser() -> ArgumentParser: """Returns a parser object for this command""" p = get_render_parser() + p.prog = "conda debug" p.description = """ Set up environments and activation scripts to debug your build or test phase. @@ -87,9 +86,9 @@ def get_parser() -> ArgumentParser: return p -def execute(): +def execute(args): parser = get_parser() - args = parser.parse_args() + args = parser.parse_args(args) try: activation_string = api.debug( @@ -119,5 +118,6 @@ def execute(): sys.exit(1) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda debug` instead.") def main(): - return execute() + return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_develop.py b/conda_build/cli/main_develop.py index ec33555748..a7a202e5ff 100644 --- a/conda_build/cli/main_develop.py +++ b/conda_build/cli/main_develop.py @@ -5,14 +5,16 @@ from conda.base.context import context, determine_target_prefix -from conda_build import api -from conda_build.conda_interface import ArgumentParser, add_parser_prefix +from .. import api +from ..conda_interface import ArgumentParser, add_parser_prefix +from ..deprecations import deprecated logging.basicConfig(level=logging.INFO) def parse_args(args): p = ArgumentParser( + prog="conda develop", description=""" Install a Python package in 'development mode'. @@ -86,5 +88,6 @@ def execute(args): ) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda develop` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_index.py b/conda_build/cli/main_index.py index 1f596b5d37..b0eefa8aa7 100644 --- a/conda_build/cli/main_index.py +++ b/conda_build/cli/main_index.py @@ -16,8 +16,9 @@ def parse_args(args): p = ArgumentParser( + prog="conda index", description="Update package index metadata files in given directories. " - "Pending deprecated, please use the standalone conda-index project." + "Pending deprecated, please use the standalone conda-index project.", ) p.add_argument( @@ -120,5 +121,6 @@ def execute(args): ) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda index` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_inspect.py b/conda_build/cli/main_inspect.py index 472bd2f3af..79e0594a4f 100644 --- a/conda_build/cli/main_inspect.py +++ b/conda_build/cli/main_inspect.py @@ -7,14 +7,16 @@ from conda.base.context import context, determine_target_prefix -from conda_build import api -from conda_build.conda_interface import ArgumentParser, add_parser_prefix +from .. import api +from ..conda_interface import ArgumentParser, add_parser_prefix +from ..deprecations import deprecated logging.basicConfig(level=logging.INFO) def parse_args(args): p = ArgumentParser( + prog="conda inspect", description="Tools for inspecting conda packages.", epilog=""" Run --help on the subcommands like 'conda inspect linkages --help' to see the @@ -220,5 +222,6 @@ def execute(args): raise ValueError(f"Unrecognized subcommand: {args.subcommand}.") +@deprecated("3.26.0", "4.0.0", addendum="Use `conda inspect` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_metapackage.py b/conda_build/cli/main_metapackage.py index 7657432fdc..71be2e7d3d 100644 --- a/conda_build/cli/main_metapackage.py +++ b/conda_build/cli/main_metapackage.py @@ -4,18 +4,16 @@ import logging import sys -from conda_build import api -from conda_build.conda_interface import ( - ArgumentParser, - add_parser_channels, - binstar_upload, -) +from .. import api +from ..conda_interface import ArgumentParser, add_parser_channels, binstar_upload +from ..deprecations import deprecated logging.basicConfig(level=logging.INFO) def parse_args(args): p = ArgumentParser( + prog="conda metapackage", description=""" Tool for building conda metapackages. A metapackage is a package with no files, only metadata. They are typically used to collect several packages @@ -114,5 +112,6 @@ def execute(args): api.create_metapackage(channel_urls=channel_urls, **args.__dict__) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda metapackage` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_render.py b/conda_build/cli/main_render.py index 15cb3bdc4b..a563e87c1b 100644 --- a/conda_build/cli/main_render.py +++ b/conda_build/cli/main_render.py @@ -8,15 +8,12 @@ import yaml from yaml.parser import ParserError -from conda_build import __version__, api -from conda_build.conda_interface import ( - ArgumentParser, - add_parser_channels, - cc_conda_build, -) -from conda_build.config import get_channel_urls, get_or_merge_config -from conda_build.utils import LoggingContext -from conda_build.variants import get_package_variants, set_language_env_vars +from .. import __version__, api +from ..conda_interface import ArgumentParser, add_parser_channels, cc_conda_build +from ..config import get_channel_urls, get_or_merge_config +from ..deprecations import deprecated +from ..utils import LoggingContext +from ..variants import get_package_variants, set_language_env_vars on_win = sys.platform == "win32" log = logging.getLogger(__name__) @@ -44,6 +41,7 @@ def __call__(self, parser, namespace, values, option_string=None): def get_render_parser(): p = ArgumentParser( + prog="conda render", description=""" Tool for expanding the template meta.yml file (containing Jinja syntax and selectors) into the rendered meta.yml files. The template meta.yml file is @@ -245,6 +243,7 @@ def execute(args, print_results=True): return metadata_tuples +@deprecated("3.26.0", "4.0.0", addendum="Use `conda render` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/cli/main_skeleton.py b/conda_build/cli/main_skeleton.py index 7642bc14da..7bb9e3369f 100644 --- a/conda_build/cli/main_skeleton.py +++ b/conda_build/cli/main_skeleton.py @@ -6,9 +6,10 @@ import pkgutil import sys -import conda_build.api as api -from conda_build.conda_interface import ArgumentParser -from conda_build.config import Config +from .. import api +from ..conda_interface import ArgumentParser +from ..config import Config +from ..deprecations import deprecated thisdir = os.path.dirname(os.path.abspath(__file__)) logging.basicConfig(level=logging.INFO) @@ -16,6 +17,7 @@ def parse_args(args): p = ArgumentParser( + prog="conda skeleton", description=""" Generates a boilerplate/skeleton recipe, which you can then edit to create a full recipe. Some simple skeleton recipes may not even need edits. @@ -60,6 +62,7 @@ def execute(args): ) +@deprecated("3.26.0", "4.0.0", addendum="Use `conda skeleton` instead.") def main(): return execute(sys.argv[1:]) diff --git a/conda_build/plugin.py b/conda_build/plugin.py new file mode 100644 index 0000000000..03a3949f44 --- /dev/null +++ b/conda_build/plugin.py @@ -0,0 +1,62 @@ +# Copyright (C) 2014 Anaconda, Inc +# SPDX-License-Identifier: BSD-3-Clause +import conda.plugins + +from .cli.main_build import execute as build +from .cli.main_convert import execute as convert +from .cli.main_debug import execute as debug +from .cli.main_develop import execute as develop +from .cli.main_index import execute as index +from .cli.main_inspect import execute as inspect +from .cli.main_metapackage import execute as metapackage +from .cli.main_render import execute as render +from .cli.main_skeleton import execute as skeleton + + +@conda.plugins.hookimpl +def conda_subcommands(): + yield conda.plugins.CondaSubcommand( + name="build", + summary="Build conda packages from a conda recipe.", + action=build, + ) + yield conda.plugins.CondaSubcommand( + name="convert", + summary="Convert pure Python packages to other platforms (a.k.a., subdirs).", + action=convert, + ) + yield conda.plugins.CondaSubcommand( + name="debug", + summary="Debug the build or test phases of conda recipes.", + action=debug, + ) + yield conda.plugins.CondaSubcommand( + name="develop", + summary="Install a Python package in 'development mode'. Similar to `pip install --editable`.", + action=develop, + ) + yield conda.plugins.CondaSubcommand( + name="index", + summary="Update package index metadata files. Pending deprecation, use https://github.com/conda/conda-index instead.", + action=index, + ) + yield conda.plugins.CondaSubcommand( + name="inspect", + summary="Tools for inspecting conda packages.", + action=inspect, + ) + yield conda.plugins.CondaSubcommand( + name="metapackage", + summary="Specialty tool for generating conda metapackage.", + action=metapackage, + ) + yield conda.plugins.CondaSubcommand( + name="render", + summary="Expand a conda recipe into a platform-specific recipe.", + action=render, + ) + yield conda.plugins.CondaSubcommand( + name="skeleton", + summary="Generate boilerplate conda recipes.", + action=skeleton, + ) diff --git a/news/4921-convert-entrypoints-to-plugins b/news/4921-convert-entrypoints-to-plugins new file mode 100644 index 0000000000..f2b9a78563 --- /dev/null +++ b/news/4921-convert-entrypoints-to-plugins @@ -0,0 +1,20 @@ +### Enhancements + +* Implement subcommands as conda plugins. (#4921) + +### Bug fixes + +* + +### Deprecations + +* Mark executable invocations (e.g., `conda-build`) as pending deprecation. (#4921) +* Mark module based invocations (e.g., `python -m conda_build.cli.main_build`) as pending deprecation. (#4921) + +### Docs + +* + +### Other + +* diff --git a/pyproject.toml b/pyproject.toml index 33b00c3a1b..9fb3b5f222 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,9 @@ conda-debug = "conda_build.cli.main_debug:main" [project.entry-points."distutils.commands"] bdist_conda = "conda_build.bdist_conda:bdist_conda" +[project.entry-points.conda] +conda-build = "conda_build.plugin" + [tool.hatch.version] source = "vcs" diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 850b742901..bc307a23a4 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -31,7 +31,7 @@ requirements: run: - beautifulsoup4 - chardet - - conda >=4.13 + - conda >=22.11.0 - conda-index - conda-package-handling >=1.3 - filelock