text: ''' A decorator-based method of constructing IPython magics with `argparse`
  option handling.
  
  New magic functions can be defined like so::
  
      from IPython.core.magic_arguments import (argument, magic_arguments,
          parse_argstring)
  
      @magic_arguments()
      @argument('-o', '--option', help='An optional argument.')
      @argument('arg', type=int, help='An integer positional argument.')
      def magic_cool(self, arg):
          """ A really cool magic command.
  
      """
          args = parse_argstring(magic_cool, arg)
          ...
  
  The `@magic_arguments` decorator marks the function as having argparse arguments.
  The `@argument` decorator adds an argument using the same syntax as argparse's
  `add_argument()` method. More sophisticated uses may also require the
  `@argument_group` or `@kwds` decorator to customize the formatting and the
  parsing.
  
  Help text for the magic is automatically generated from the docstring and the
  arguments::
  
      In[1]: %cool?
          %cool [-o OPTION] arg
          
          A really cool magic command.
          
          positional arguments:
            arg                   An integer positional argument.
          
          optional arguments:
            -o OPTION, --option OPTION
                                  An optional argument.
  
  Here is an elaborated example that uses default parameters in `argument` and calls the `args` in the cell magic::
  
      from IPython.core.magic import register_cell_magic
      from IPython.core.magic_arguments import (argument, magic_arguments,
                                              parse_argstring)
  
  
      @magic_arguments()
      @argument(
          "--option",
          "-o",
          help=("Add an option here"),
      )
      @argument(
          "--style",
          "-s",
          default="foo",
          help=("Add some style arguments"),
      )
      @register_cell_magic
      def my_cell_magic(line, cell):
          args = parse_argstring(my_cell_magic, line)
          print(f"{args.option=}")
          print(f"{args.style=}")
          print(f"{cell=}")
  
  In a jupyter notebook, this cell magic can be executed like this::
  
      %%my_cell_magic -o Hello
      print("bar")
      i = 42
  
  Inheritance diagram:
  
  .. inheritance-diagram:: IPython.core.magic_arguments
     :parts: 3
  
  '''
  #-----------------------------------------------------------------------------
  # Copyright (C) 2010-2011, IPython Development Team.
  #
  # Distributed under the terms of the Modified BSD License.
  #
  # The full license is in the file COPYING.txt, distributed with this software.
  #-----------------------------------------------------------------------------
  import argparse
  import re
  
  # Our own imports
  from IPython.core.error import UsageError
  from IPython.utils.decorators import undoc
  from IPython.utils.process import arg_split
  from IPython.utils.text import dedent
  
  NAME_RE = re.compile(r"[a-zA-Z][a-zA-Z0-9_-]*$")
  
  @undoc
  class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
      """A HelpFormatter with a couple of changes to meet our needs.
      """
      # Modified to dedent text.
      def _fill_text(self, text, width, indent):
          return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
  
      # Modified to wrap argument placeholders in <> where necessary.
      def _format_action_invocation(self, action):
          if not action.option_strings:
              metavar, = self._metavar_formatter(action, action.dest)(1)
              return metavar
  
          else:
              parts = []
  
              # if the Optional doesn't take a value, format is:
              #    -s, --long
              if action.nargs == 0:
                  parts.extend(action.option_strings)
  
              # if the Optional takes a value, format is:
              #    -s ARGS, --long ARGS
              else:
                  default = action.dest.upper()
                  args_string = self._format_args(action, default)
                  # IPYTHON MODIFICATION: If args_string is not a plain name, wrap
                  # it in <> so it's valid RST.
                  if not NAME_RE.match(args_string):
                      args_string = "<%s>" % args_string
                  for option_string in action.option_strings:
                      parts.append('%s %s' % (option_string, args_string))
  
              return ', '.join(parts)
  
      # Override the default prefix ('usage') to our % magic escape,
      # in a code block.
      def add_usage(self, usage, actions, groups, prefix="::\n\n  %"):
          super(MagicHelpFormatter, self).add_usage(usage, actions, groups, prefix)
  
  class MagicArgumentParser(argparse.ArgumentParser):
      """ An ArgumentParser tweaked for use by IPython magics.
      """
      def __init__(self,
                   prog=None,
                   usage=None,
                   description=None,
                   epilog=None,
                   parents=None,
                   formatter_class=MagicHelpFormatter,
                   prefix_chars='-',
                   argument_default=None,
                   conflict_handler='error',
                   add_help=False):
          if parents is None:
              parents = []
          super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
              description=description, epilog=epilog,
              parents=parents, formatter_class=formatter_class,
              prefix_chars=prefix_chars, argument_default=argument_default,
              conflict_handler=conflict_handler, add_help=add_help)
  
      def error(self, message):
          """ Raise a catchable error instead of exiting.
          """
          raise UsageError(message)
  
      def parse_argstring(self, argstring):
          """ Split a string into an argument list and parse that argument list.
          """
          argv = arg_split(argstring)
          return self.parse_args(argv)
  
  
  def construct_parser(magic_func):
      """ Construct an argument parser using the function decorations.
      """
      kwds = getattr(magic_func, 'argcmd_kwds', {})
      if 'description' not in kwds:
          kwds['description'] = getattr(magic_func, '__doc__', None)
      arg_name = real_name(magic_func)
      parser = MagicArgumentParser(arg_name, **kwds)
      # Reverse the list of decorators in order to apply them in the
      # order in which they appear in the source.
      group = None
      for deco in magic_func.decorators[::-1]:
          result = deco.add_to_parser(parser, group)
          if result is not None:
              group = result
  
      # Replace the magic function's docstring with the full help text.
      magic_func.__doc__ = parser.format_help()
  
      return parser
  
  
  def parse_argstring(magic_func, argstring):
      """ Parse the string of arguments for the given magic function.
      """
      return magic_func.parser.parse_argstring(argstring)
  
  
  def real_name(magic_func):
      """ Find the real name of the magic.
      """
      magic_name = magic_func.__name__
      if magic_name.startswith('magic_'):
          magic_name = magic_name[len('magic_'):]
      return getattr(magic_func, 'argcmd_name', magic_name)
  
  
  class ArgDecorator(object):
      """ Base class for decorators to add ArgumentParser information to a method.
      """
  
      def __call__(self, func):
          if not getattr(func, 'has_arguments', False):
              func.has_arguments = True
              func.decorators = []
          func.decorators.append(self)
          return func
  
      def add_to_parser(self, parser, group):
          """ Add this object's information to the parser, if necessary.
          """
          pass
  
  
  class magic_arguments(ArgDecorator):
      """ Mark the magic as having argparse arguments and possibly adjust the
      name.
      """
  
      def __init__(self, name=None):
          self.name = name
  
      def __call__(self, func):
          if not getattr(func, 'has_arguments', False):
              func.has_arguments = True
              func.decorators = []
          if self.name is not None:
              func.argcmd_name = self.name
          # This should be the first decorator in the list of decorators, thus the
          # last to execute. Build the parser.
          func.parser = construct_parser(func)
          return func
  
  
  class ArgMethodWrapper(ArgDecorator):
  
      """
      Base class to define a wrapper for ArgumentParser method.
  
      Child class must define either `_method_name` or `add_to_parser`.
  
      """
  
      _method_name = None
  
      def __init__(self, *args, **kwds):
          self.args = args
          self.kwds = kwds
  
      def add_to_parser(self, parser, group):
          """ Add this object's information to the parser.
          """
          if group is not None:
              parser = group
          getattr(parser, self._method_name)(*self.args, **self.kwds)
          return None
  
  
  class argument(ArgMethodWrapper):
      """ Store arguments and keywords to pass to add_argument().
  
      Instances also serve to decorate command methods.
      """
      _method_name = 'add_argument'
  
  
  class defaults(ArgMethodWrapper):
      """ Store arguments and keywords to pass to set_defaults().
  
      Instances also serve to decorate command methods.
      """
      _method_name = 'set_defaults'
  
  
  class argument_group(ArgMethodWrapper):
      """ Store arguments and keywords to pass to add_argument_group().
  
      Instances also serve to decorate command methods.
      """
  
      def add_to_parser(self, parser, group):
          """ Add this object's information to the parser.
          """
          return parser.add_argument_group(*self.args, **self.kwds)
  
  
  class kwds(ArgDecorator):
      """ Provide other keywords to the sub-parser constructor.
      """
      def __init__(self, **kwds):
          self.kwds = kwds
  
      def __call__(self, func):
          func = super(kwds, self).__call__(func)
          func.argcmd_kwds = self.kwds
          return func
  
  
  __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
      'parse_argstring']
  