Skip to content

Commit

Permalink
Vendor click_default_group to make us installable again (#540)
Browse files Browse the repository at this point in the history
* Vendor click_default_group to make us installable again

Fixes #492

Co-authored-by: Heungsub Lee <19982+sublee@users.noreply.github.com>

* Add news fragment

---------

Co-authored-by: Heungsub Lee <19982+sublee@users.noreply.github.com>
  • Loading branch information
hynek and sublee authored Aug 4, 2023
1 parent 66134cc commit 3717dbf
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 6 deletions.
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ classifiers = [
requires-python = ">=3.8"
dependencies = [
"click",
"click-default-group",
"importlib-resources>=5; python_version<'3.10'",
"incremental",
"jinja2",
Expand Down Expand Up @@ -146,9 +145,9 @@ strict = true
exclude = '^src/towncrier/test/test_.*\.py$'

[[tool.mypy.overrides]]
module = 'click_default_group'
# 2022-09-04: This library has no type annotations.
ignore_missing_imports = true
module = 'towncrier.click_default_group'
# Vendored module without type annotations.
ignore_errors = true

[[tool.mypy.overrides]]
module = 'incremental'
Expand All @@ -174,4 +173,5 @@ exclude_lines = [
omit = [
"src/towncrier/__main__.py",
"src/towncrier/test/*",
"src/towncrier/click_default_group.py",
]
3 changes: 1 addition & 2 deletions src/towncrier/_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@

import click

from click_default_group import DefaultGroup

from ._version import __version__
from .build import _main as _build_cmd
from .check import _main as _check_cmd
from .click_default_group import DefaultGroup
from .create import _main as _create_cmd


Expand Down
148 changes: 148 additions & 0 deletions src/towncrier/click_default_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# SPDX-FileCopyrightText: 2015 Heungsub Lee <19982+sublee@users.noreply.github.com>
#
# SPDX-License-Identifier: BSD-3-Clause

# Vendored from
# https://github.com/click-contrib/click-default-group/tree/b671ae5325d186fe5ea7abb584f15852a1e931aa
# Because the PyPI package could not be installed on modern Pips anymore and
# the project looks unmaintaintained.

"""
click_default_group
~~~~~~~~~~~~~~~~~~~
Define a default subcommand by `default=True`:
.. sourcecode:: python
import click
from click_default_group import DefaultGroup
@click.group(cls=DefaultGroup, default_if_no_args=True)
def cli():
pass
@cli.command(default=True)
def foo():
click.echo('foo')
@cli.command()
def bar():
click.echo('bar')
Then you can invoke that without explicit subcommand name:
.. sourcecode:: console
$ cli.py --help
Usage: cli.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Command:
foo*
bar
$ cli.py
foo
$ cli.py foo
foo
$ cli.py bar
bar
"""
import warnings

import click


__all__ = ["DefaultGroup"]
__version__ = "1.2.2"


class DefaultGroup(click.Group):
"""Invokes a subcommand marked with `default=True` if any subcommand not
chosen.
:param default_if_no_args: resolves to the default command if no arguments
passed.
"""

def __init__(self, *args, **kwargs):
# To resolve as the default command.
if not kwargs.get("ignore_unknown_options", True):
raise ValueError("Default group accepts unknown options")
self.ignore_unknown_options = True
self.default_cmd_name = kwargs.pop("default", None)
self.default_if_no_args = kwargs.pop("default_if_no_args", False)
super().__init__(*args, **kwargs)

def set_default_command(self, command):
"""Sets a command function as the default command."""
cmd_name = command.name
self.add_command(command)
self.default_cmd_name = cmd_name

def parse_args(self, ctx, args):
if not args and self.default_if_no_args:
args.insert(0, self.default_cmd_name)
return super().parse_args(ctx, args)

def get_command(self, ctx, cmd_name):
if cmd_name not in self.commands:
# No command name matched.
ctx.arg0 = cmd_name
cmd_name = self.default_cmd_name
return super().get_command(ctx, cmd_name)

def resolve_command(self, ctx, args):
base = super()
cmd_name, cmd, args = base.resolve_command(ctx, args)
if hasattr(ctx, "arg0"):
args.insert(0, ctx.arg0)
cmd_name = cmd.name
return cmd_name, cmd, args

def format_commands(self, ctx, formatter):
formatter = DefaultCommandFormatter(self, formatter, mark="*")
return super().format_commands(ctx, formatter)

def command(self, *args, **kwargs):
default = kwargs.pop("default", False)
decorator = super().command(*args, **kwargs)
if not default:
return decorator
warnings.warn(
"Use default param of DefaultGroup or " "set_default_command() instead",
DeprecationWarning,
)

def _decorator(f):
cmd = decorator(f)
self.set_default_command(cmd)
return cmd

return _decorator


class DefaultCommandFormatter:
"""Wraps a formatter to mark a default command."""

def __init__(self, group, formatter, mark="*"):
self.group = group
self.formatter = formatter
self.mark = mark

def __getattr__(self, attr):
return getattr(self.formatter, attr)

def write_dl(self, rows, *args, **kwargs):
rows_ = []
for cmd_name, help in rows:
if cmd_name == self.group.default_cmd_name:
rows_.insert(0, (cmd_name + self.mark, help))
else:
rows_.append((cmd_name, help))
return self.formatter.write_dl(rows_, *args, **kwargs)
1 change: 1 addition & 0 deletions src/towncrier/newsfragments/540.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Towncrier now vendors the click-default-group package that prevented installations on modern Pips.

0 comments on commit 3717dbf

Please sign in to comment.