Skip to content

rewrite the parser #2205

Open
Open

Description

@davidism

Click currently uses a modified copy of optparse to parse command line arguments. In order to use it, Click creates an entire optparse class structure mirroring the structure that users write, which seems like unnecessary overhead. Click also goes beyond what optparse was intended for, which means there's a lot of working around how optparse works. There are old issues about the processing being an issue, and newer features are also affected because it's hard to manage whether the parser or the processing will get values or raise errors.

Most of the code in our parser copy isn't even the actual parsing code, it's all the optparse equivalent objects to Click's objects. I've started working on a new parser that works directly from the Click objects, instead of constructing a separate parser structure.

The following open issues are related, although I'm not guaranteeing that they'll all be immediately fixed with the rewrite. Some of them are also affected by the processing pipeline, which happens after parsing.

Activity

  1. self-assigned this
    on Feb 23, 2022
  2. added
    parsingParsing, parameters, commands, chaining, context
    on Feb 23, 2022
  3. davidism commented on Mar 5, 2022

    @davidism
    MemberAuthor

    I've got the parser rewritten, and am going through making the tests pass. Things I've noticed so far:

    • The documentation around the syntax that is parsed, how different settings can interact, and how you can get into ambiguous or confusing situations with combinations of settings and option names, needs way clearer documentation.
    • Even if replacing the parser has no visible effects, I'm going to do it in a 9.0 release, issuing deprecation warnings for related code in 8.1.
    • OptionParser is exported at the top level, I'll use module-level __getattr__ to show a deprecation warning.
    • There is no reason to have a distinction between BaseCommand and Command. It seems it was done to allow commands that used other parsers, but nothing about Command prevents that, the developer could already override parse_args without needing BaseCommand. It also makes type annotations more complicated. I'll deprecate BaseCommand in the same way I did BaseRequest in Werkzeug.
    • Similarly, there's probably not a need to distinguish MultiCommand from Group, but I haven't looked at that as much yet.
    • Context has three settings, allow_extra_args, allow_interspersed_args, and ignore_unknown_options, that all take defaults based on class attributes on Command and Group, but can also be overridden by context_settings. These settings affect how the parser works, and should probably not be exposed to users. Mainly, allow_extra_args should not be a documented way to capture arguments in user code, as it mixes the state the processing pipeline works with for sub/chained commands with information the user might want. Instead, if users explicitly want extra args, they should use argument(nargs=-1). (On a side note, it's weird that one of them is ignore and the other two are allow.)
    • There doesn't seem to be a reason for the distinction between ctx.args and ctx.protected_args. They are split after parameter parsing and processing, then immediately re-joined in the invoke step. The split was introduced as part of chaining at some point, but it's not clear why it was need for it, I removed it and everything still works.
    • To accommodate how the current parser works, some parsing errors are raised immediately, and some are handled in later processing.
    • The exceptions are not very consistent in terms of how they're created, whether they generate a message themselves, and what information they track. There could probably be fewer exception subclasses.
    • There is no way to parse args without processing them. It might be useful to extract that
    • There is way more customizability than is documented. For example, after parsing, each parameter processes its value. But that processing is passed the list of extra args, and can return a modified list, so parameter processing can affect other parameters and extra args.
  4. added this to the 9.0.0 milestone on Aug 17, 2023
  5. ofek commented on Sep 16, 2024

    @ofek
    Contributor

    Has this been merged?

  6. davidism commented on Sep 16, 2024

    @davidism
    MemberAuthor

    no, I haven't had time to finish it yet

  7. ofek commented on Sep 16, 2024

    @ofek
    Contributor

    Ah okay, just wanted to check! Do you happen to have an estimate of the potential decrease in import/load time?

  8. ofek commented on Mar 28, 2025

    @ofek
    Contributor
    • Context has three settings, allow_extra_args, allow_interspersed_args, and ignore_unknown_options, that all take defaults based on class attributes on Command and Group, but can also be overridden by context_settings. These settings affect how the parser works, and should probably not be exposed to users.

    I use ignore_unknown_options very frequently to capture arbitrary arguments (without users having to always supply --) in order to pass everything to some other CLI. Please don't remove that one 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

parsingParsing, parameters, commands, chaining, context

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions