Вопрос

I'm using Tornado options to define command-line arguments. However, I would like to be able to throw arbitrary configuration options, not defined in code, to my program. These will differ, depending on what the program is supposed to do. For instance, connect to a bluetooth device using a MAC address or connect to a serial device using a TTY.

If I define a set of "mandatory" options in code and then add an additional when calling the program, I get an exception thrown by parse_command_line().

It would be very handy to get e.g. a dictionary with the remaining (undefined) options. That is, much in the same way as **kwargs works in functions.

Can this be done?

(A work-around is to define a string option named e.g. configuration and throw everything in there, possibly encoded in some clever way. As the program is being called by another program I can e.g. base64-encode a serialized dict.)


Update: I've noticed that if you add command-line args without leading dashes, Tornado will ignore them and return a list with remaining (undefined) options.

Это было полезно?

Решение

You can create subclass of OptionParser singleton and modify parsing method. Example:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import OptionParser
import sys

class MyOptionParser(OptionParser):
    def parse_command_line(self, args=None, final=True):
        if args is None:
            args = sys.argv
        remaining = []
        for i in range(1, len(args)):
            # All things after the last option are command line arguments
            if not args[i].startswith("-"):
                remaining = args[i:]
                break
            if args[i] == "--":
                remaining = args[i + 1:]
                break
            arg = args[i].lstrip("-")
            name, equals, value = arg.partition("=")
            name = name.replace('-', '_')
            if not name in self._options:
#   modified line       self.print_help()
#   modified line       raise Error('Unrecognized command line option: %r' % name)
                self.define(name, help="Arbitrary option") # new line
            option = self._options[name]
            if not equals:
                if option.type == bool:
                    value = "true"
                else:
                    raise Error('Option %r requires a value' % name)
            option.parse(value)

        if final:
            self.run_parse_callbacks()

        return remaining

options = MyOptionParser()
options.define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(options.myoption) 

if __name__ == "__main__":
    options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

The only difference from source is I'm defining passed option instead of throwing an Exception. Running:

python test.py --myoption="test"

Другие советы

The tornado.options philosophy is that any module may define options, not just the main entry point. So if you might need a bluetooth mac address, you'd define that option in the module that interacts with bluetooth. (and if you might need more than one you can set multiple=True). The only tricky part is that you must import all modules that define options before calling parse_command_line. Truly arbitrary options are not supported by tornado.options.

It's also possible to use argparse or another command-line library instead of tornado.options.parse_command_line - the rest of tornado doesn't care whether you're using tornado.options or not.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top