def list_templates(config):
- """List available templates
+ """List available templates.
:param config: a :class:`.Config` object.
def heads(config, verbose=False, resolve_dependencies=False):
- """Show current available heads in the script directory
+ """Show current available heads in the script directory.
:param config: a :class:`.Config` instance.
positional = spec[0][1:]
kwarg = []
- subparser = subparsers.add_parser(fn.__name__, help=fn.__doc__)
+ # parse first line(s) of helptext without a line break
+ help_ = fn.__doc__
+ if help_:
+ help_text = []
+ for line in help_.split("\n"):
+ if not line.strip():
+ break
+ else:
+ help_text.append(line.strip())
+ else:
+ help_text = ""
+ subparser = subparsers.add_parser(
+ fn.__name__, help=" ".join(help_text)
+ )
add_options(subparser, positional, kwarg)
subparser.set_defaults(cmd=(fn, positional, kwarg))
self.parser = parser
--- /dev/null
+.. change::
+ :tags: bug, commands
+ :tickets: 552
+
+ Fixed bug introduced in release 0.9.0 where the helptext for commands
+ inadvertently got expanded to include function docstrings from the
+ command.py module. The logic has been adjusted to only refer to the first
+ line(s) preceding the first line break within each docstring, as was the
+ original intent.
from contextlib import contextmanager
+import inspect
from io import BytesIO
from io import TextIOWrapper
import re
finally:
config.command.revision = orig_revision
eq_(canary.mock_calls, [mock.call(self.cfg, message="foo")])
+
+ def test_help_text(self):
+ commands = {
+ fn.__name__
+ for fn in [getattr(command, n) for n in dir(command)]
+ if inspect.isfunction(fn)
+ and fn.__name__[0] != "_"
+ and fn.__module__ == "alembic.command"
+ }
+ # make sure we found them
+ assert commands.intersection(
+ {"upgrade", "downgrade", "merge", "revision"}
+ )
+
+ # catch help text coming intersection
+ with mock.patch("alembic.config.ArgumentParser") as argparse:
+ config.CommandLine()
+ for kall in argparse().add_subparsers().mock_calls:
+ for sub_kall in kall.call_list():
+ if sub_kall[0] == "add_parser":
+ cmdname = sub_kall[1][0]
+ help_text = sub_kall[2]["help"]
+ if help_text:
+ commands.remove(cmdname)
+ # more than two spaces
+ assert not re.search(r" ", help_text)
+
+ # no markup stuff
+ assert ":" not in help_text
+
+ # no newlines
+ assert "\n" not in help_text
+
+ # ends with a period
+ assert help_text.endswith(".")
+
+ # not too long
+ assert len(help_text) < 80
+ assert not commands, "Commands without help text: %s" % commands