Skip to content

Only plain literals should be parsed #97

Description

@mbarkhau

@dbieber correctly pointed out in #95 that there is a more general problem with the parsing code. This is a follow up issue to that issue which only dealt with the most common case of a single argument.

Here are some cases demonstrating

$ python3 -c "import fire.parser as p; v = p.DefaultParseValue('[1+1]'); print(type(v), v)"
<class 'list'> [2]
$ python3 -c "import fire.parser as p; v = p.DefaultParseValue('{key: 1+1}'); print(type(v), v)"
<class 'dict'> {'key': 2} 

Some options are:

  1. Accept the existing behaviour.
  2. Always parse the whole argument as a string by doing raise ValueError in ``fire.parser._LiteralEval`
  3. Parse only the sub expressions as strings

For 2. and 3. I think these would be the valid types from the ast module.

  • Str
  • Name (converted to Str)
  • Num
  • List
  • Tuple
  • Dict
  • Set
  • UnaryOp

Activity

  1. dbieber commented on Oct 12, 2017

    @dbieber
    Collaborator

    First, something that confuses me:
    The literal_eval documentation says it is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

    This made me wonder, why does ast.literal_eval("1+1") evaluate to 2? A: It doesn't (in Python 2).
    So why does DefaultParseValue('[1+1]') give [2]? A: It doesn't (in Python 2).
    Turns out this is a Python 2 vs Python 3 distinction. ast.literal_eval("1+1") does give 2 in Python 3 :(.

    So if we can get a port of Python 2's literal_eval into Python 3, I think that resolves our issues and we can remove the BinOp special case altogether. I haven't investigated whether this exists yet.


    Aside:
    Something to keep in the back of your mind as you think about this: it would be amazing if there were a standard way of parsing literals from the command line that was consistent across languages. This way if someone were to implement, say, Java Fire, and if someone were to implement piping args from the command line into Fire CLIs, then Fire could become a mechanism for interoperating across languages.

  2. dbieber commented on Oct 12, 2017

    @dbieber
    Collaborator

    The Python 3 documentation for literal_eval also suggests it won't parse expressions with operators.
    The discussion at https://bugs.python.org/issue22525 suggests the reason that + and - are supported is to support complex literals like 4+2i. I don't think that's sufficient justification for considering "1+1" a literal; from my understanding, "1+1" is not a literal and so I'm inclined to think this is a Python 3 bug.

  3. dbieber commented on Oct 12, 2017

    @dbieber
    Collaborator

    Opened issue in Python bug tracker at https://bugs.python.org/issue31778

  4. dbieber commented on Oct 17, 2017

    @dbieber
    Collaborator

    Even if this issue with Python is fixed, we'll still want Fire to work with builds of Python that have the current behavior. So, we should walk the AST and check for BinOps ourself, and raise a ValueError if there is a BinOp in our AST.

    The ast.literal_eval code (Python 2.7.3 and Python 3) may be useful for reference.

  5. dbieber commented on Dec 5, 2017

    @dbieber
    Collaborator

    The next steps here are to check for BinOps in the ast of the input. This will happen here:

    if isinstance(root.body, ast.BinOp):
    raise ValueError(value)

    If there are any BinOps in the ast (this requires traversing the ast to determine), then we will raise a ValueError as above, rather than parsing this non-literal. The only exception to this is that the BinOps + and - used in the context of expressing a complex number (e.g. 2 + 3j) are allowed.

    The Python 2.7.3 implementation of literal_eval demonstrates how we can do this AST walk to determine if any non-allowed BinOps are present.

  6. alexshadley commented on Oct 9, 2018

    @alexshadley
    Contributor

    Looks like I've got some spare time in the next week, I'll give this one a whirl.

  7. dbieber commented on Oct 29, 2018

    @dbieber
    Collaborator

    Hey Alex, keep us posted if you try anything.

  8. bcluyse commented on Jan 15, 2020

    @bcluyse

    Hi, can I have a look at this issue?

  9. alexshadley commented on Jan 21, 2020

    @alexshadley
    Contributor

    Yes! Sorry for the long radio silence, I should've commented earlier that I was no longer working on this one.

  10. Alex0AI commented on Oct 9, 2026

    @Alex0AI

    I plan to address the remaining top-level complex-number inconsistency: DefaultParseValue("1+2j") returns a string, while DefaultParseValue("[1+2j]") returns a complex value in a list. The supported Python versions already reject ordinary arithmetic in ast.literal_eval. I will add regression coverage for complex values, nested containers, and non-literal expressions before submitting. This investigation and comment are assisted by OpenAI Codex.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions