Search code examples
pythoncommand-line-interfacehttpiedocopt

Implementing HTTPie in docopt


I'm trying to implement HTTPie's CLI with docopt but for some reason I'm unable to make METHOD optional as the first argument. I've removed most of the options to isolate this issue.

Additionally, the way the author of HTTPie gets REQUEST ITEM seems wrong with the docopt docs so was wondering what's the best alternative to implement that piece.

The complete docs for HTTPie: https://github.com/jkbr/httpie

My docopt example: https://gist.github.com/dasickis/4711926


Solution

  • docopt is greedy when tries to match a pattern. So if you pattern is:

    usage: http [METHOD] URL [ITEM...]
    

    And you run $ http google.com bla then docopt will match google.com as METHOD, because, yo, that's the first positional argument! docopt has no information in this case to deduct how METHOD should look like. However if you have the following usage:

    usage: http [get|post|put|delete] URL [ITEM...]
    

    Then it knows that if it isn't get/post/put/delete, then it should be a URL. However, I can see how this is not optimal: 1. httpie uses upper-case names (which are interpreted as positional arguments, not commands by docopt), 2. If there are many commands, the usage might become a long line.

    If you are not concerned with compatibility with httpie, I would go with (A) lower-case commands, since commands are by convention lower-case. Another non-compatible design decision would be to (B) require METHOD:

    usage: http METHOD URL [ITEM...]
    

    This way you make the usage-pattern non-ambiguous. (C) What I would do to keep compatibility, I would make the following usage:

    usage: http URL [ITEM...] 
           http METHOD URL [ITEM...]
    

    In this case the second sub-pattern will never match (docopt doesn't know anything about urls and methods, which makes the first patter a superset of the second), and I would then decompose the arguments as:

    positional_arguments = [args['URL']] + args['ITEM']
    

    and then parse positional_arguments manually, depending on whether the first argument looks like url, or like a request verb.

    Sorry that docopt doesn't handle this case elegantly, I will think more about this use-case. If you have any suggestions, please, create an issue: https://github.com/docopt/docopt/issues

    P.S. You are right that REQUEST ITEM is wrong and very non-conventional and confusing. It should have been either REQUEST-ITEM or REQUEST_ITEM or just ITEM.