#!/usr/bin/env python
from setuptools import setup
setup(
  name = 'cs.cmdutils',
  author = 'Cameron Simpson',
  author_email = 'cs@cskk.id.au',
  version = '20210404',
  url = 'https://bitbucket.org/cameron_simpson/css/commits/all',
  description =
    ('a `BaseCommand` class for constructing command lines, some convenience '    
 'functions for working with the `cmd` module, and some other command line '    
 'related stuff'),
  long_description =
    ('Convenience functions for working with the Cmd module\n'    
 'and other command line related stuff.\n'    
 '\n'    
 '*Latest release 20210404*:\n'    
 'BaseCommand subclasses: automatically add the main usage message to the '    
 'subclass docstring.\n'    
 '\n'    
 '## Class `BaseCommand`\n'    
 '\n'    
 'A base class for handling nestable command lines.\n'    
 '\n'    
 'This class provides the basic parse and dispatch mechanisms\n'    
 'for command lines.\n'    
 'To implement a command line\n'    
 'one instantiates a subclass of `BaseCommand`:\n'    
 '\n'    
 '    class MyCommand(BaseCommand):\n'    
 "        GETOPT_SPEC = 'ab:c'\n"    
 '        USAGE_FORMAT = r"""Usage: {cmd} [-a] [-b bvalue] [-c] [--] '    
 'arguments...\n'    
 '          -a    Do it all.\n'    
 '          -b    But using bvalue.\n'    
 "          -c    The 'c' option!\n"    
 '        """\n'    
 '        ...\n'    
 '\n'    
 'Running a command is done by:\n'    
 '\n'    
 '    MyCommand(argv).run()\n'    
 '\n'    
 'Modules which implement a command line mode generally look like:\n'    
 '\n'    
 '    ... imports etc ...\n'    
 '    def main(argv=None):\n'    
 '        return MyCommand(argv).run()\n'    
 '    ... other code ...\n'    
 '    class MyCommand(BaseCommand):\n'    
 '    ... other code ...\n'    
 "    if __name__ == '__main__':\n"    
 '        sys.exit(main(sys.argv))\n'    
 '\n'    
 'Instances have a `self.options` attribute on which optional\n'    
 'modes are set,\n'    
 'avoiding conflict with the attributes of `self`.\n'    
 '\n'    
 'Subclasses with no subcommands\n'    
 'generally just implement a `main(argv)` method.\n'    
 '\n'    
 'Subclasses with subcommands\n'    
 'should implement a `cmd_`*subcommand*`(argv)` method\n'    
 'for each subcommand.\n'    
 'If there is a paragraph in the method docstring\n'    
 'commencing with `Usage:`\n'    
 'then that paragraph is incorporated automatically\n'    
 'into the main usage message.\n'    
 'Example:\n'    
 '\n'    
 '    def cmd_ls(self, argv):\n'    
 '        """ Usage: {cmd} [paths...]\n'    
 '              Emit a listing for the named paths.\n'    
 '\n'    
 '            Further docstring non-usage information here.\n'    
 '        """\n'    
 '        ... do the "ls" subcommand ...\n'    
 '\n'    
 'The subclass is customised by overriding the following methods:\n'    
 '* `apply_defaults()`:\n'    
 '  prepare the initial state of `self.options`\n'    
 '  before any command line options are applied.\n'    
 '* `apply_opt(opt,val)`:\n'    
 '  apply an individual getopt global command line option\n'    
 '  to `self.options`.\n'    
 '* `apply_opts(opts)`:\n'    
 '  apply the `opts` to `self.options`.\n'    
 '  `opts` is an `(option,value)` sequence\n'    
 '  as returned by `getopot.getopt`.\n'    
 '  The default implementation iterates over these and calls `apply_opt`.\n'    
 '* `cmd_`*subcmd*`(argv)`:\n'    
 '  if the command line options are followed by an argument\n'    
 '  whose value is *subcmd*,\n'    
 '  then the method `cmd_`*subcmd*`(subcmd_argv)`\n'    
 '  will be called where `subcmd_argv` contains the command line arguments\n'    
 '  following *subcmd*.\n'    
 '* `main(argv)`:\n'    
 '  if there are no command line aguments after the options\n'    
 '  or the first argument does not have a corresponding\n'    
 '  `cmd_`*subcmd* method\n'    
 '  then method `main(argv)`\n'    
 '  will be called where `argv` contains the command line arguments.\n'    
 '* `run_context()`:\n'    
 '  a context manager to provide setup or teardown actions\n'    
 '  to occur before and after the command implementation respectively,\n'    
 '  such as to open and close a database.\n'    
 '\n'    
 'Editorial: why not arparse?\n'    
 'Primarily because when incorrectly invoked\n'    
 'an argparse command line prints the help/usage messgae\n'    
 'and aborts the whole programme with `SystemExit`.\n'    
 '\n'    
 '### Method `BaseCommand.__init__(self, argv=None, *, cmd=None, '    
 '**kw_options)`\n'    
 '\n'    
 'Initialise the command line.\n'    
 'Raises `GetoptError` for unrecognised options.\n'    
 '\n'    
 'Parameters:\n'    
 '* `argv`:\n'    
 '  optional command line arguments\n'    
 '  including the main command name if `cmd` is not specified.\n'    
 '  The default is `sys.argv`.\n'    
 '  The contents of `argv` are copied,\n'    
 '  permitting desctructive parsing of `argv`.\n'    
 '* `options`:\n'    
 '  a optional object for command state and context.\n'    
 '  If not specified a new `SimpleNamespace`\n'    
 '  is allocated for use as `options`,\n'    
 '  and prefilled with `.cmd` set to `cmd`\n'    
 '  and other values as set by `.apply_defaults()`\n'    
 '  if such a method is provided.\n'    
 '* `cmd`:\n'    
 '  optional command name for context;\n'    
 '  if this is not specified it is taken from `argv.pop(0)`.\n'    
 'Other keyword arguments are applied to `self.options`\n'    
 'as attributes.\n'    
 '\n'    
 'The command line arguments are parsed according to\n'    
 "the optional `GETOPT_SPEC` class attribute (default `''`).\n"    
 'If `getopt_spec` is not empty\n'    
 'then `apply_opts(opts)` is called\n'    
 'to apply the supplied options to the state\n'    
 'where `opts` is the return from `getopt.getopt(argv,getopt_spec)`.\n'    
 '\n'    
 'After the option parse,\n'    
 'if the first command line argument *foo*\n'    
 'has a corresponding method `cmd_`*foo*\n'    
 'then that argument is removed from the start of `argv`\n'    
 'and `self.cmd_`*foo*`(argv,options,cmd=`*foo*`)` is called\n'    
 'and its value returned.\n'    
 'Otherwise `self.main(argv,options)` is called\n'    
 'and its value returned.\n'    
 '\n'    
 'If the command implementation requires some setup or teardown\n'    
 'then this may be provided by the `run_context`\n'    
 'context manager method,\n'    
 'called with `cmd=`*subcmd* for subcommands\n'    
 'and with `cmd=None` for `main`.\n'    
 '\n'    
 '### `BaseCommand.OPTIONS_CLASS`\n'    
 '\n'    
 '### Method `BaseCommand.__init_subclass__()`\n'    
 '\n'    
 'Update subclasses of `BaseCommand`.\n'    
 '\n'    
 'Appends the usage message to the class docstring.\n'    
 '\n'    
 '### Method `BaseCommand.apply_defaults(self)`\n'    
 '\n'    
 'Stub `apply_defaults` method.\n'    
 '\n'    
 'Subclasses can override this to set up the initial state of `self.options`.\n'    
 '\n'    
 '### Method `BaseCommand.apply_opt(self, opt, val)`\n'    
 '\n'    
 'Handle a individual global command line option.\n'    
 '\n'    
 '### Method `BaseCommand.apply_opts(self, opts)`\n'    
 '\n'    
 'Apply command line options.\n'    
 '\n'    
 '### Method `BaseCommand.cmd_help(argv, options)`\n'    
 '\n'    
 'Usage: {cmd} [subcommand-names...]\n'    
 'Print the help for the named subcommands,\n'    
 'or for all subcommands if no names are specified.\n'    
 '\n'    
 '### Method `BaseCommand.getopt_error_handler(cmd, options, e, usage)`\n'    
 '\n'    
 'The `getopt_error_handler` method\n'    
 'is used to control the handling of `GetoptError`s raised\n'    
 'during the command line parse\n'    
 'or during the `main` or `cmd_`*subcmd*` calls.\n'    
 '\n'    
 'The handler is called with these parameters:\n'    
 '* `cmd`: the command name\n'    
 '* `options`: the `options` object\n'    
 '* `e`: the `GetoptError` exception\n'    
 '* `usage`: the command usage or `None` if this was not provided\n'    
 '\n'    
 'It returns a true value if the exception is considered handled,\n'    
 'in which case the main `run` method returns 2.\n'    
 'It returns a false value if the exception is considered unhandled,\n'    
 'in which case the main `run` method reraises the `GetoptError`.\n'    
 '\n'    
 'This default handler prints an error message to standard error,\n'    
 'prints the usage message (if specified) to standard error,\n'    
 'and returns `True` to indicate that the error has been handled.\n'    
 '\n'    
 'To let the exceptions out unhandled\n'    
 'this can be overridden with a method which just returns `False`\n'    
 'or even by setting the `getopt_error_handler` attribute to `None`.\n'    
 '\n'    
 'Otherwise,\n'    
 'the handler may perform any suitable action\n'    
 'and return `True` to contain the exception\n'    
 'or `False` to cause the exception to be reraised.\n'    
 '\n'    
 '### Method `BaseCommand.run(self)`\n'    
 '\n'    
 'Run a command.\n'    
 'Returns the exit status of the command.\n'    
 'May raise `GetoptError` from subcommands.\n'    
 '\n'    
 'If the first command line argument *foo*\n'    
 'has a corresponding method `cmd_`*foo*\n'    
 'then that argument is removed from the start of `argv`\n'    
 'and `self.cmd_`*foo*`(cmd=`*foo*`)` is called\n'    
 'and its value returned.\n'    
 'Otherwise `self.main(argv)` is called\n'    
 'and its value returned.\n'    
 '\n'    
 'If the command implementation requires some setup or teardown\n'    
 'then this may be provided by the `run_context`\n'    
 'context manager method,\n'    
 'called with `cmd=`*subcmd* for subcommands\n'    
 'and with `cmd=None` for `main`.\n'    
 '\n'    
 '### Method `BaseCommand.run_context()`\n'    
 '\n'    
 'Stub context manager which surrounds `main` or `cmd_`*subcmd*.\n'    
 '\n'    
 '### Method `BaseCommand.subcommand_usage_text(subcmd, fulldoc=False, '    
 'usage_format_mapping=None)`\n'    
 '\n'    
 'Return the usage text for a subcommand.\n'    
 '\n'    
 'Parameters:\n'    
 '* `subcmd`: the subcommand name\n'    
 '* `fulldoc`: if true (default `False`)\n'    
 '  return the full docstring with the Usage section expanded\n'    
 '  otherwise just return the Usage section.\n'    
 '\n'    
 '### Method `BaseCommand.subcommands()`\n'    
 '\n'    
 'Return a mapping of subcommand names to class attributes\n'    
 'for attributes which commence with `cls.SUBCOMMAND_METHOD_PREFIX`\n'    
 "by default `'cmd_'`.\n"    
 '\n'    
 '### Method `BaseCommand.usage_text(*a, **kw)`\n'    
 '\n'    
 'Compute the "Usage:" message for this class\n'    
 'from the top level `USAGE_FORMAT`\n'    
 "and the `'Usage:'`-containing docstrings\n"    
 'from its `cmd_*` methods.\n'    
 '\n'    
 'This is a cached method because it tries to update the\n'    
 'method docstrings after formatting, which is bad if it\n'    
 'happens more than once.\n'    
 '\n'    
 '## Function `docmd(dofunc)`\n'    
 '\n'    
 'Decorator for `cmd.Cmd` subclass methods\n'    
 'to supply some basic quality of service.\n'    
 '\n'    
 'This decorator:\n'    
 '- wraps the function call in a `cs.pfx.Pfx` for context\n'    
 '- intercepts `getopt.GetoptError`s, issues a `warning`\n'    
 '  and runs `self.do_help` with the method name,\n'    
 '  then returns `None`\n'    
 '- intercepts other `Exception`s,\n'    
 '  issues an `exception` log message\n'    
 '  and returns `None`\n'    
 '\n'    
 'The intended use is to decorate `cmd.Cmd` `do_`* methods:\n'    
 '\n'    
 '    from cmd import Cmd\n'    
 '    from cs.cmdutils import docmd\n'    
 '    ...\n'    
 '    class MyCmd(Cmd):\n'    
 '      @docmd\n'    
 '      def do_something(...):\n'    
 '        ... do something ...\n'    
 '\n'    
 '# Release Log\n'    
 '\n'    
 '\n'    
 '\n'    
 '*Release 20210404*:\n'    
 'BaseCommand subclasses: automatically add the main usage message to the '    
 'subclass docstring.\n'    
 '\n'    
 '*Release 20210306*:\n'    
 '* BREAKING CHANGE: rework BaseCommand as a more normal class instantiated '    
 'with argv and with most methods being instance methods, getting the former '    
 '`options` parameter from self.options.\n'    
 '* BaseCommand: provide default `apply_opt` and `apply_opts` methods; '    
 'subclasses will generally just override the former.\n'    
 '\n'    
 '*Release 20210123*:\n'    
 'BaseCommand: propagate the format mapping (cmd, USAGE_KEYWORDS) to the '    
 'subusage generation.\n'    
 '\n'    
 '*Release 20201102*:\n'    
 '* BaseCommand.cmd_help: supply usage only for "all commands", full docstring '    
 'for specified commands.\n'    
 '* BaseCommand: honour presupplied options.log_level.\n'    
 '* BaseCommand.usage_text: handle missing USAGE_FORMAT better.\n'    
 '* BaseCommand.run: provide options.upd.\n'    
 '* BaseCommand subclasses may now override BaseCommand.OPTIONS_CLASS (default '    
 'SimpleNamespace) in order to provide convenience methods on the options.\n'    
 '* BaseCommand.run: separate variable for subcmd with dash translated to '    
 'underscore to match method names.\n'    
 '* Minor fixes.\n'    
 '\n'    
 '*Release 20200615*:\n'    
 'BaseCommand.usage_text: do not mention the "help" command if it is the only '    
 "subcommand (it won't be available if there are no other subcommands).\n"    
 '\n'    
 '*Release 20200521.1*:\n'    
 'Fix DISTINFO.install_requires.\n'    
 '\n'    
 '*Release 20200521*:\n'    
 '* BaseCommand.run: support using BaseCommand subclasses as cmd_* names to '    
 'make it easy to nest BaseCommands.\n'    
 '* BaseCommand: new hack_postopts_argv method called after parsing the main '    
 'command line options, for inferring subcommands or the like.\n'    
 '* BaseCommand: extract "Usage:" paragraphs from subcommand method docstrings '    
 'to build the main usage message.\n'    
 '* BaseCommand: new cmd_help default command.\n'    
 '* Assorted bugfixes and small improvements.\n'    
 '\n'    
 '*Release 20200318*:\n'    
 '* BaseCommand.run: make argv optional, get additional usage keywords from '    
 'self.USAGE_KEYWORDS.\n'    
 '* @BaseCommand.add_usage_to_docstring: honour cls.USAGE_KEYWORDS.\n'    
 '* BaseCommand: do not require GETOPT_SPEC for commands with no defined '    
 'options.\n'    
 '* BaseCommand.run: call cs.logutils.setup_logging.\n'    
 '\n'    
 '*Release 20200229*:\n'    
 'Improve subcommand selection logic, replace StackableValues with stackattrs, '    
 'drop `cmd` from arguments passed to main/cmd_* methods (present in '    
 '`options`).\n'    
 '\n'    
 '*Release 20200210*:\n'    
 '* New BaseCommand.add_usage_to_docstring class method to be called after '    
 'class setup, to append the usage message to the class docstring.\n'    
 '* BaseCommand.run: remove spurious Pfx(cmd), as logutils does this for us '    
 'already.\n'    
 '\n'    
 '*Release 20190729*:\n'    
 'BaseCommand: support for a USAGE_FORMAT usage message format string and a '    
 'getopt_error_handler method.\n'    
 '\n'    
 '*Release 20190619.1*:\n'    
 'Another niggling docstring formatting fix.\n'    
 '\n'    
 '*Release 20190619*:\n'    
 'Minor documentation updates.\n'    
 '\n'    
 '*Release 20190617.2*:\n'    
 'Lint.\n'    
 '\n'    
 '*Release 20190617.1*:\n'    
 'Initial release with @docmd decorator and alpha quality BaseCommand command '    
 'line assistance class.'),
  classifiers = ['Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)'],
  install_requires = ['cs.context', 'cs.deco', 'cs.gimmicks', 'cs.lex', 'cs.logutils', 'cs.pfx', 'cs.py.doc', 'cs.resources'],
  keywords = ['python2', 'python3'],
  license = 'GNU General Public License v3 or later (GPLv3+)',
  long_description_content_type = 'text/markdown',
  package_dir = {'': 'lib/python'},
  py_modules = ['cs.cmdutils'],
)
