argparse missing optparse capabilities?

Discussion in 'Python' started by rurpy@yahoo.com, Jan 5, 2012.

  1. Guest

    I have optparse code that parses a command line containing
    intermixed positional and optional arguments, where the optional
    arguments set the context for the following positional arguments.
    For example,

    myprogram.py arg1 -c33 arg2 arg3 -c44 arg4

    'arg1' is processed in a default context, 'args2' and 'arg3' in
    context '33', and 'arg4' in context '44'.

    I am trying to do the same using argparse but it appears to be
    not doable in a documented way.

    Here is the working optparse code (which took 30 minutes to write
    using just the optparse docs):

    import optparse
    def append_with_pos (option, opt_str, value, parser):
    if getattr (parser.values, option.dest, None) is None:
    setattr (parser.values, option.dest, [])
    getattr (parser.values, option.dest).append ((value, len
    (parser.largs)))
    def opt_parse():
    p = optparse.OptionParser()
    p.add_option ("-c", type=int,
    action='callback', callback=append_with_pos)
    opts, args = p.parse_args()
    return args, opts
    if __name__ == '__main__':
    args, opts = opt_parse()
    print args, opts

    Output from the command line above:
    ['arg1', 'arg2', 'arg3', 'arg4'] {'c': [(33, 1), (44, 3)]}
    The -c values are stored as (value, arglist_position) tuples.

    Here is an attempt to convert to argparse using the guidelines
    in the argparse docs:

    import argparse
    class AppendWithPos (argparse.Action):
    def __call__ (self, parser, namespace, values,
    option_string=None):
    if getattr (namespace, self.dest, None) is None:
    setattr (namespace, self.dest, [])
    getattr (namespace, self.dest).extend ((values, len
    (parser.largs)))
    def arg_parse():
    p = argparse.ArgumentParser (description='description')
    p.add_argument ('src', nargs='*')
    p.add_argument ('-c', type=int, action=AppendWithPos)
    opts = p.parse_args()
    return opts
    if __name__ == '__main__':
    opts = arg_parse()
    print opts

    This fails with,
    AttributeError: 'ArgumentParser' object has no attribute 'largs'
    and of course, the argparse.parser is not documented beyond how
    to instantiate it. Even were that not a problem, argparse complains
    about "unrecognised arguments" for any positional arguments that
    occur after an optional one. I've been farting with this code for
    a day now.

    Any suggestions on how I can convince argparse to do what optparse
    does easily will be very welcome. (I tried parse_known_args() but
    that breaks help and requires me to detect truly unknown arguments.)

    (Python 2.7.1 if it matters and apologies if Google mangles
    the formatting of this post.)
     
    , Jan 5, 2012
    #1
    1. Advertising

  2. Guest

    On Jan 5, 1:05 am, "" <> wrote:
    >   class AppendWithPos (argparse.Action):
    >     def __call__ (self, parser, namespace, values,
    > option_string=None):
    >         if getattr (namespace, self.dest, None) is None:
    >             setattr (namespace, self.dest, [])
    >         getattr (namespace, self.dest).extend ((values, len (parser.largs)))


    I realized right after posting that the above line should
    be I think,
    getattr (namespace, self.dest).extend ((values, len
    (namespace.src)))

    but that still doesn't help with the "unrecognised arguments"
    problem.
     
    , Jan 5, 2012
    #2
    1. Advertising

  3. Am 05.01.2012 09:05, schrieb :
    > I have optparse code that parses a command line containing
    > intermixed positional and optional arguments, where the optional
    > arguments set the context for the following positional arguments.
    > For example,
    >
    > myprogram.py arg1 -c33 arg2 arg3 -c44 arg4
    >
    > 'arg1' is processed in a default context, 'args2' and 'arg3' in
    > context '33', and 'arg4' in context '44'.


    Question: How would you e.g. pass the string "-c33" as first argument,
    i.e. to be parsed in the default context?

    The point is that you separate the parameters in a way that makes it
    possible to parse them in a way that works 100%, not just a way that
    works in 99% of all cases. For that reason, many commandline tools
    accept "--" as separator, so that "cp -- -r -x" will copy the file "-r"
    to the folder "-x". In that light, I would consider restructuring your
    commandline.

    > I am trying to do the same using argparse but it appears to be
    > not doable in a documented way.


    As already hinted at, I don't think this is possible and that that is so
    by design.

    Sorry..

    Uli
     
    Ulrich Eckhardt, Jan 5, 2012
    #3
  4. Guest

    On 01/05/2012 02:19 AM, Ulrich Eckhardt wrote:
    > Am 05.01.2012 09:05, schrieb :
    >> I have optparse code that parses a command line containing
    >> intermixed positional and optional arguments, where the optional
    >> arguments set the context for the following positional arguments.
    >> For example,
    >>
    >> myprogram.py arg1 -c33 arg2 arg3 -c44 arg4
    >>
    >> 'arg1' is processed in a default context, 'args2' and 'arg3' in
    >> context '33', and 'arg4' in context '44'.

    >
    > Question: How would you e.g. pass the string "-c33" as first argument,
    > i.e. to be parsed in the default context?


    There will not be a need for that.

    > The point is that you separate the parameters in a way that makes it
    > possible to parse them in a way that works 100%, not just a way that
    > works in 99% of all cases.


    I agree that one should strive for a syntax that "works
    100%" but in this case, the simplicity and intuitiveness
    of the existing command syntax outweigh by far the need
    for having it work in very improbable corner cases.
    (And I'm sure I've seen this syntax used in other unix
    command line tools in the past though I don't have time
    to look for examples now.)

    If argparse does not handle this syntax for some such
    purity reason (as opposed to, for example. it is hard
    to do in argparse's current design) then argparse is
    mistakenly putting purity before practicality.

    > For that reason, many commandline tools
    > accept "--" as separator, so that "cp -- -r -x" will copy the file "-r"
    > to the folder "-x". In that light, I would consider restructuring your
    > commandline.


    In my case that's not possible since I am replacing an
    existing tool with a Python application and changing the
    command line syntax is not an option.

    >> I am trying to do the same using argparse but it appears to be
    >> not doable in a documented way.

    >
    > As already hinted at, I don't think this is possible and that that is so
    > by design.


    Thanks for the confirmation. I guess that shows that
    optparse has a reason to exist beyond backwards compatibility.
     
    , Jan 5, 2012
    #4
  5. Ian Kelly Guest

    On Thu, Jan 5, 2012 at 1:05 AM, <> wrote:
    > I have optparse code that parses a command line containing
    > intermixed positional and optional arguments, where the optional
    > arguments set the context for the following positional arguments.
    > For example,
    >
    >  myprogram.py arg1 -c33 arg2 arg3 -c44 arg4
    >
    > 'arg1' is processed in a default context, 'args2' and 'arg3' in
    > context '33', and 'arg4' in context '44'.
    >
    > I am trying to do the same using argparse but it appears to be
    > not doable in a documented way.
    >
    > Here is the working optparse code (which took 30 minutes to write
    > using just the optparse docs):
    >
    >  import optparse
    >  def append_with_pos (option, opt_str, value, parser):
    >        if getattr (parser.values, option.dest, None) is None:
    >            setattr (parser.values, option.dest, [])
    >        getattr (parser.values, option.dest).append ((value, len
    > (parser.largs)))
    >  def opt_parse():
    >        p = optparse.OptionParser()
    >        p.add_option ("-c", type=int,
    >            action='callback', callback=append_with_pos)
    >        opts, args = p.parse_args()
    >        return args, opts
    >  if __name__ == '__main__':
    >        args, opts = opt_parse()
    >        print args, opts
    >
    > Output from the command line above:
    >  ['arg1', 'arg2', 'arg3', 'arg4'] {'c': [(33, 1), (44, 3)]}
    > The -c values are stored as (value, arglist_position) tuples.
    >
    > Here is an attempt to convert to argparse using the guidelines
    > in the argparse docs:
    >
    >  import argparse
    >  class AppendWithPos (argparse.Action):
    >    def __call__ (self, parser, namespace, values,
    > option_string=None):
    >        if getattr (namespace, self.dest, None) is None:
    >            setattr (namespace, self.dest, [])
    >        getattr (namespace, self.dest).extend ((values, len
    > (parser.largs)))
    >  def arg_parse():
    >        p = argparse.ArgumentParser (description='description')
    >        p.add_argument ('src', nargs='*')
    >        p.add_argument ('-c', type=int, action=AppendWithPos)
    >        opts = p.parse_args()
    >        return opts
    >  if __name__ == '__main__':
    >        opts = arg_parse()
    >        print opts
    >
    > This fails with,
    >  AttributeError: 'ArgumentParser' object has no attribute 'largs'
    > and of course, the argparse.parser is not documented beyond how
    > to instantiate it.  Even were that not a problem, argparse complains
    > about "unrecognised arguments" for any positional arguments that
    > occur after an optional one.  I've been farting with this code for
    > a day now.
    >
    > Any suggestions on how I can convince argparse to do what optparse
    > does easily will be very welcome.  (I tried parse_known_args() but
    > that breaks help and requires me to detect truly unknown arguments.)
    >
    > (Python 2.7.1 if it matters and apologies if Google mangles
    > the formatting of this post.)


    You have the namespace object in your custom action. Instead of
    "len(parser.largs)", couldn't you just do "len(namespace.src)"?

    Cheers,
    Ian
     
    Ian Kelly, Jan 5, 2012
    #5
  6. Ian Kelly Guest

    On Thu, Jan 5, 2012 at 11:14 AM, Ian Kelly <> wrote:
    > On Thu, Jan 5, 2012 at 1:05 AM, <> wrote:
    >> I have optparse code that parses a command line containing
    >> intermixed positional and optional arguments, where the optional
    >> arguments set the context for the following positional arguments.
    >> For example,
    >>
    >>  myprogram.py arg1 -c33 arg2 arg3 -c44 arg4
    >>
    >> 'arg1' is processed in a default context, 'args2' and 'arg3' in
    >> context '33', and 'arg4' in context '44'.
    >>
    >> I am trying to do the same using argparse but it appears to be
    >> not doable in a documented way.
    >>
    >> Here is the working optparse code (which took 30 minutes to write
    >> using just the optparse docs):
    >>
    >>  import optparse
    >>  def append_with_pos (option, opt_str, value, parser):
    >>        if getattr (parser.values, option.dest, None) is None:
    >>            setattr (parser.values, option.dest, [])
    >>        getattr (parser.values, option.dest).append ((value, len
    >> (parser.largs)))
    >>  def opt_parse():
    >>        p = optparse.OptionParser()
    >>        p.add_option ("-c", type=int,
    >>            action='callback', callback=append_with_pos)
    >>        opts, args = p.parse_args()
    >>        return args, opts
    >>  if __name__ == '__main__':
    >>        args, opts = opt_parse()
    >>        print args, opts
    >>
    >> Output from the command line above:
    >>  ['arg1', 'arg2', 'arg3', 'arg4'] {'c': [(33, 1), (44, 3)]}
    >> The -c values are stored as (value, arglist_position) tuples.
    >>
    >> Here is an attempt to convert to argparse using the guidelines
    >> in the argparse docs:
    >>
    >>  import argparse
    >>  class AppendWithPos (argparse.Action):
    >>    def __call__ (self, parser, namespace, values,
    >> option_string=None):
    >>        if getattr (namespace, self.dest, None) is None:
    >>            setattr (namespace, self.dest, [])
    >>        getattr (namespace, self.dest).extend ((values, len
    >> (parser.largs)))
    >>  def arg_parse():
    >>        p = argparse.ArgumentParser (description='description')
    >>        p.add_argument ('src', nargs='*')
    >>        p.add_argument ('-c', type=int, action=AppendWithPos)
    >>        opts = p.parse_args()
    >>        return opts
    >>  if __name__ == '__main__':
    >>        opts = arg_parse()
    >>        print opts
    >>
    >> This fails with,
    >>  AttributeError: 'ArgumentParser' object has no attribute 'largs'
    >> and of course, the argparse.parser is not documented beyond how
    >> to instantiate it.  Even were that not a problem, argparse complains
    >> about "unrecognised arguments" for any positional arguments that
    >> occur after an optional one.  I've been farting with this code for
    >> a day now.
    >>
    >> Any suggestions on how I can convince argparse to do what optparse
    >> does easily will be very welcome.  (I tried parse_known_args() but
    >> that breaks help and requires me to detect truly unknown arguments.)
    >>
    >> (Python 2.7.1 if it matters and apologies if Google mangles
    >> the formatting of this post.)

    >
    > You have the namespace object in your custom action.  Instead of
    > "len(parser.largs)", couldn't you just do "len(namespace.src)"?


    Sorry, I missed the second part of that. You seem to be right, as far
    as I can tell from tinkering with it, all the positional arguments
    have to be in a single group. If you have some positional arguments
    followed by an option followed by more positional arguments, and any
    of the arguments have a loose nargs quantifier ('?' or '*' or '+'),
    then you get an error.
     
    Ian Kelly, Jan 5, 2012
    #6
  7. Guest

    On 01/05/2012 11:46 AM, Ian Kelly wrote:
    > On Thu, Jan 5, 2012 at 11:14 AM, Ian Kelly <> wrote:
    >> On Thu, Jan 5, 2012 at 1:05 AM, <> wrote:
    >>> I have optparse code that parses a command line containing
    >>> intermixed positional and optional arguments, where the optional
    >>> arguments set the context for the following positional arguments.
    >>> For example,
    >>>
    >>> myprogram.py arg1 -c33 arg2 arg3 -c44 arg4
    >>>
    >>> 'arg1' is processed in a default context, 'args2' and 'arg3' in
    >>> context '33', and 'arg4' in context '44'.
    >>>
    >>> I am trying to do the same using argparse but it appears to be
    >>> not doable in a documented way.

    >[...]
    >
    > Sorry, I missed the second part of that. You seem to be right, as far
    > as I can tell from tinkering with it, all the positional arguments
    > have to be in a single group. If you have some positional arguments
    > followed by an option followed by more positional arguments, and any
    > of the arguments have a loose nargs quantifier ('?' or '*' or '+'),
    > then you get an error.


    OK, thanks for the second confirmation. I was hoping there
    was something I missed or some undocumented option to allow
    intermixed optional and positional arguments with Argparse
    but it appears not.

    I notice that Optparse seems to intentionally provide this
    capability since it offers a "disable_interspersed_args()"
    method. It is unfortunate that Argparse chose to not to
    provide backward compatibility for this thus forcing some
    users to continue using a deprecated module.
     
    , Jan 6, 2012
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric
    Replies:
    4
    Views:
    2,663
  2. Jeff Keasler

    optparse functionality missing

    Jeff Keasler, Jun 20, 2008, in forum: Python
    Replies:
    1
    Views:
    243
    Mark Wooding
    Jun 21, 2008
  3. samwyse

    optparse/argparse for cgi/wsgi?

    samwyse, Dec 10, 2010, in forum: Python
    Replies:
    0
    Views:
    250
    samwyse
    Dec 10, 2010
  4. Thorsten Kampe
    Replies:
    3
    Views:
    1,906
    Thorsten Kampe
    May 21, 2011
  5. Evan Driscoll

    Introspecting optparse/argparse objects

    Evan Driscoll, Jan 11, 2012, in forum: Python
    Replies:
    2
    Views:
    234
    Evan Driscoll
    Jan 12, 2012
Loading...

Share This Page