Type conversions #58

flying-sheep opened this Issue Oct 4, 2012 · 3 comments


None yet

2 participants


I thought about what docopt does right and where that approach has its drawbacks.

I came to the conclusion that it’s a little too fast for its own good when it comes to the speed at which you get something (the dictionary) out of it: docopt is designed to be used in 1 function call on 1 docstring you wrote, but since that one also needs to be human readable, it has its shortcomings.

My problem is type conversions: Though roundabout, after using argparse you have an object containing ints, files, and everything you can instantiate by feeding it strings.

Compare argparse’s options.x with docopt’s int(options["<x>"]). docopt is beautiful and easy until you have the dict, but ugly afterwards.

The other problem is “break early and often”: if you convert stuff whenever it is needed, it is there where your program will break if fed invalid options. If you decide to do this as early as possible (e.g. by converting your dictionary into a set of variables), you have to do if-else branches again and handle missing arguments (e.g. if an optional path to a file is not given, you have to do f = file(options['--path']) if options['--path'] else None)

so what do i want?

  • a way to easily typeconvert and validate while directly after docopting.

what is “easy”?

  • not doing it manually, but letting docopt do it. either by another function, or by another keyword argument. This should of course handle lists by just mapping the conversion onto them.

    (e.g. docopt(__doc__, conversions={'<x>': int, '<y>': int})

  • letting docopt handle missing optional arguments: if something isn’t given, it will yield None in the dict, which docopt shouldn’t typeconvert, as mist constructors do silly stuff or break when given None. (for lists, that’s easy. map(int, []) ist still [])

keleshev commented Oct 5, 2012

int(options["<x>"]) is what I have in some of my throw-away scripts, and I'm not totally happy with this.

However there is little overhead in using schema. Compare:

  • your API:
args = docopt(__doc__, conversions={'<x>': int, '<y>': int})
  • docopt with schema:
Int = Or(None, Use(int))
args = Schema({'<x>': Int, '<y>': Int}).validate(docopt(__doc__))

And the more complex your CLI is—the smaller is the relative overhead.

Also, I have never seen a program that takes arbitrary integers—except for calculators—it is always a range or a set, which you can express easily with schema:

schema = Schema({'<x>': And(Use(int), lambda n: n in [2**n for n in range(10)]))

As said on Reddit: I'm almost convinced, yet I still think it's not DRY that way: you have to tell schema which arguments are optional, while docopt already knows about that.

keleshev commented Oct 8, 2012

As I said somewhere before—I prefer to err on the side of simplicity. Anyway, thanks for suggestion—more of that is always welcome.

@keleshev keleshev closed this Oct 8, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment