Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python + argparse - how to get order of optional arguments from command line

I would like to know how to get order of optional argument passed from commandline to argparse

I have image processing class which is able to apply different actions to image - like rotate, crop, resize...

And order in which these actions are applied is often essential (for example: you want to crop image before you resize it)

I have this code:

parser = argparse.ArgumentParser(description='Image processing arguments')

parser.add_argument('source_file', help='source file')
parser.add_argument('target_file', help='target file')

parser.add_argument('-resize', nargs=2, help='resize image', metavar=('WIDTH', 'HEIGHT'))
parser.add_argument('-rotate', nargs=1, help='rotate image', metavar='ANGLE')
parser.add_argument('-crop', nargs=4, help='crop image', metavar=('START_X','START_Y','WIDTH','HEIGHT'))

ar = parser.parse_args()

print ar

But - no matter in which order I pass parameters to script:

cmd.py test.jpg test2.jpg -crop 10 10 200 200 -resize 450 300

cmd.py test.jpg test2.jpg -resize 450 300 -crop 10 10 200 200

in Namespace items are always in same order (alphabetical I suppose):

Namespace(crop=['10', '10', '200', '200'], resize=['450', '300'], rotate=None, source_file='test.jpg', target_file='test
2.jpg')

Is there way to order them by position in command line string or to get their index?

like image 945
Skach Avatar asked Oct 12 '11 09:10

Skach


People also ask

Does order matter in Argparse?

Combining Positional and Optional arguments Note that the order does not matter.

How do you make Argparse argument optional?

Optional Arguments To add an optional argument, simply omit the required parameter in add_argument() .

Why do we use Argparse in Python?

The argparse module makes it easy to write user-friendly command-line interfaces. The program defines what arguments it requires, and argparse will figure out how to parse those out of sys.argv .


2 Answers

You could always peek at sys.argv which is a list (and thus ordered) and simply iterate over it checking which argument comes first or use the list.index() to see the respective positions of your keywords in the list...

sys.argv contains a list of the words entered in the command line (the delimiter of such "word" is a space unless a string was surrounded by quotation marks). This means that if the user entered something like ./my_proggie -resize 500 then sys.argv would contain a list like this: ['./my_proggie', '-resize', '500'].

like image 87
immortal Avatar answered Oct 01 '22 19:10

immortal


The Namespace is a simple object whose str() lists its attributes according to the order of the keys in its __dict__. Attributes are set with setattr(namespace, dest, value).

One solution is to define a custom Namespace class. For example:

class OrderNamespace(argparse.Namespace):
    def __init__(self, **kwargs):
        self.__dict__['order'] = []
        super(OrderNamespace, self).__init__(**kwargs)
    def __setattr__(self,attr,value):
        self.__dict__['order'].append(attr)
        super(OrderNamespace, self).__setattr__(attr, value)

and use

args = parser.parse_args(None, OrderNamespace())

producing for your two examples

OrderNamespace(crop=..., order=[..., 'crop', 'resize'], resize=...)
OrderNamespace(crop=..., order=[..., 'resize', 'crop'], resize=...)

The order attribute gives the order in which the other attributes are set. The initial items are for defaults and the file positionals. Adding default=argparse.SUPPRESS to the arguments will suppress some of these items. This custom class could be more elaborate, using for example an OrderedDictionary, only noting the order for selected arguments, or using order to control the display of the attributes.

Another option is to use a custom Action class that creates this order attribute, e.g.

class OrderAction(argparse._StoreAction):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values)
        order = getattr(namespace, 'order') if hasattr(namespace, 'order') else []
        order.append(self.dest)
        setattr(namespace, 'order', order)
like image 20
hpaulj Avatar answered Oct 01 '22 19:10

hpaulj