• 
      

    Fixed issues with rbtools argument parsing on Python 3 when running '--help'

    Review Request #10856 — Created Jan. 24, 2020 and submitted

    Information

    RBTools
    release-2.0.x
    392c050...

    Reviewers

    On python 2, running rbt --help <command> and rbt <command> --help
    are equivalent. However, running rbt <command> --help does not work
    on python 3. This happens because byte strings are utilized in the
    code to see if --help or -h are passed in as arguments. They are
    never found because the code is trying to find b'--help' or b'-h'.
    In python 2, b'--help' is equivalent to '--help', but in python 3,
    this is not the case.

    To fix this issue, the byte strings b'--help' and b’-h’ have been
    changed to python 3 compatible strings (i.e. --help and -h). Unit
    tests have also been added to ensure that all possible positions and
    cases of the 'help' argument work for rbtools.(ex. rbt —help <command>,
    rbt <command> -h, rbt help <command>, etc.).

    All tests were run using both python 2.7.17 and python 3.7.6.

    Test cases run on python 2.7.17 and 3.7.6:
    Test case where rbt help <command> is run
    Test case where rbt <command> --help or rbt <command> -h is run
    Test case where rbt --help <command> or rbt -h <command> is run
    Test case where an invalid rbt command is run with help
    Test case where multiple arguments are entered

    Description From Last Updated

    Can you also detail which versions of Python all your tests have been run under? We need to ensure full …

    chipx86chipx86

    Can you wrap the change description at 79 columns? It would also be nice to use backticks whenever you're showing …

    chipx86chipx86

    E501 line too long (91 > 79 characters)

    reviewbotreviewbot

    E501 line too long (91 > 79 characters)

    reviewbotreviewbot

    E501 line too long (91 > 79 characters)

    reviewbotreviewbot

    E501 line too long (94 > 79 characters)

    reviewbotreviewbot

    E501 line too long (80 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (82 > 79 characters)

    reviewbotreviewbot

    E501 line too long (88 > 79 characters)

    reviewbotreviewbot

    E501 line too long (80 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (92 > 79 characters)

    reviewbotreviewbot

    E501 line too long (82 > 79 characters)

    reviewbotreviewbot

    E501 line too long (88 > 79 characters)

    reviewbotreviewbot

    E501 line too long (80 > 79 characters)

    reviewbotreviewbot

    E501 line too long (82 > 79 characters)

    reviewbotreviewbot

    I think this docstring and file could be named in a more descriptive way eg. Tests for RBTools help command …

    hxqlinhxqlin

    Similar issue about naming here - I think something like HelpCommandTests would be better

    hxqlinhxqlin

    I think this could be better named as test_help_command. Ordering it that way would make it more consistent with the …

    hxqlinhxqlin

    Maybe this could be pulled out into a function since you use it a few times

    hxqlinhxqlin

    This snippet feels like it could also be a function since you use it a few times. Also, maybe command_string …

    hxqlinhxqlin

    Same as above

    hxqlinhxqlin

    Minor but maybe naming test_help to be test_command would be better here to be more consistent with your first test …

    hxqlinhxqlin

    Same as above

    hxqlinhxqlin

    Same as above

    hxqlinhxqlin

    Same as above

    hxqlinhxqlin

    Since you're testing help already, it might be a good idea to add tests for when there are more than …

    hxqlinhxqlin

    W293 blank line contains whitespace

    reviewbotreviewbot

    W293 blank line contains whitespace

    reviewbotreviewbot

    W293 blank line contains whitespace

    reviewbotreviewbot

    W293 blank line contains whitespace

    reviewbotreviewbot

    W391 blank line at end of file

    reviewbotreviewbot

    This is wrapping way too early. Can you make sure it wraps at 79 characters? Same with docstrings below.

    chipx86chipx86

    I think you can go ahead and just use these strings inline. It results in less code, and is easier …

    chipx86chipx86

    Blank line between blocks/statements. Same below.

    chipx86chipx86

    Docstring summaries are limited to one line. Same below.

    chipx86chipx86

    The string type is going to be required. Should be unicode. Same below.

    chipx86chipx86

    "Returns" blocks are in the form of: Returns: type: description So probably unicode for the type.

    chipx86chipx86

    We use %-formatting, instead of .format(). It's technically faster.

    chipx86chipx86

    Blank line between each argument.

    chipx86chipx86

    bool

    chipx86chipx86

    bool

    chipx86chipx86

    W291 trailing whitespace

    reviewbotreviewbot

    W291 trailing whitespace

    reviewbotreviewbot

    W291 trailing whitespace

    reviewbotreviewbot

    W291 trailing whitespace

    reviewbotreviewbot

    I've been thinking about these code blocks, which are almost universal in your code. I think what would be better …

    chipx86chipx86

    How about making this one statement? Same below.

    chipx86chipx86

    This could be one line now. Same with ones below.

    chipx86chipx86

    Sorry, I wasn't clear in my description above. We'd want a blank line when separating blocks on the same indentation …

    chipx86chipx86

    This can be one statement.

    chipx86chipx86

    Here, and below, it'll be a lot easier to debug if we're passing in the command directly to _check_help_output. The …

    chipx86chipx86

    This should be "Check". We use a form of "Do this, do that" rather than "Does this, does that." in …

    chipx86chipx86

    The type should be (bool, optional), since it's not required.

    chipx86chipx86

    True should be shown as a literal, using double-backticks.

    chipx86chipx86

    Functions that don't return a value shouldn't have a Returns section.

    chipx86chipx86

    This seems fragile. I'm not wild about it. The idea is to have a version of command that didn't have …

    chipx86chipx86

    Rather than defining the variable, you can move the assertIn into those if statements.

    chipx86chipx86

    This is only here for the execute command, right? In this case, let's only wrap as much as we need …

    chipx86chipx86
    Checks run (1 failed, 1 succeeded)
    flake8 failed.
    JSHint passed.

    flake8

    kpatenio
    hxqlin
    1. 
        
    2. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      I think this docstring and file could be named in a more descriptive way eg. Tests for RBTools help command and test_help.py. That way it's also consistent with the other test file I see in that directory (test_post.py).

    3. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Similar issue about naming here - I think something like HelpCommandTests would be better

    4. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      I think this could be better named as test_help_command. Ordering it that way would make it more consistent with the couple of other tests with a similar pattern in the name in test_post.py

    5. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
       
       
       
       
       
      Show all issues

      Maybe this could be pulled out into a function since you use it a few times

    6. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
       
       
       
       
      Show all issues

      This snippet feels like it could also be a function since you use it a few times.

      Also, maybe command_string and test could be renamed to something more descriptive, such as expected_command_string and test_result - it wasn't very clear to me that test contained the output of executing the commands.

    7. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Same as above

    8. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Minor but maybe naming test_help to be test_command would be better here to be more consistent with your first test where you referred to a single command as test_command or changing the first one to test_help (but imo test_command is easier to understand)

    9. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Same as above

    10. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Same as above

    11. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Same as above

    12. rbtools/commands/tests/test_main.py (Diff revision 2)
       
       
      Show all issues

      Since you're testing help already, it might be a good idea to add tests for when there are more than than three arguments eg. rbt help alias abcd and also less than eg. rbt help

    13. 
        
    kpatenio
    Review request changed
    Change Summary:

    Addressed review changes and added a new test case

    Testing Done:
       

    Testing Done:

        Test case where 'rbt help <command>' is run
        Test case where 'rbt <command> --help' or 'rbt <command> -h' is run
        Test case where 'rbt --help <command>' or 'rbt -h <command>' is run
    ~   Test case where an invalid rbt command is run with 'help'

      ~ Test case where an invalid rbt command is run with 'help'
      + Test case where multiple arguments are entered

    Commit:
    a7f62a85769b8ca6fac60ae945edec61c742c533
    883fd61c35b3699bdeced4956cf8d3f739ea5ac6

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    kpatenio
    chipx86
    1. First pass, addressing some of the syntax and standards stuff.

    2. Show all issues

      Can you also detail which versions of Python all your tests have been run under? We need to ensure full coverage.

    3. Show all issues

      Can you wrap the change description at 79 columns?

      It would also be nice to use backticks whenever you're showing commands, arguments, or Python code, to help readability.

      1. Just curious, is there a way to show a 79 characters guideline for review board? I can't tell by sight alone
        whether or not my decriptions are 79 characters or shorter. (Except for IDEs. I'm able to do that easily).

        And whoops, yeah I forgot about changing the backticks for the description. The reason I used single quotes
        was because I originally made this my commit message. Whenever I committed, anything wrapped in backticks ran
        as if they ran on the command line (the output would be printed onto the commit message).

        Shoule I leave the single quotes on my commit message? Or should I update it to make it not rely on single quotes?

        (Also thanks for the review!)

      2. No there's not. It's been a long-standing feature request that I'd love to have in in some form, both for the description fields and for the diff viewer.

        Part of the problem is customizability. Once you start highlighting a column limitation, people will want to customize what column that is, and we've never figured out what the right approach is there. If it's per-user, great (and probably the right place to start). If people want to enforce it, it gets tricky.

        Regarding the backticks, are you saying that when you write a commit message and include backticks, the code in those backticks get executed on save? If so, that's very weird, and we should diagnose that in chat.

        The quotes otherwise look fine right now in the Description.

      3. I see, good to know! And yes, the code in the backticks would get executed once I saved a commit message. For example, I had help (in backticks) somewhere in my commit message,. After entering my commit message, instead of displaying 'help', it printed the following:

        GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
        These shell commands are defined internally.  Type `help' to see this list.
        Type `help name' to find out more about the function `name'.
        Use `info bash' to find out more about the shell in general.
        Use `man -k' or `info' to find out more about commands not in this list.
        
        ...
        

        Quite odd indeed.

    4. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
       
      Show all issues

      This is wrapping way too early. Can you make sure it wraps at 79 characters?

      Same with docstrings below.

    5. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
       
      Show all issues

      I think you can go ahead and just use these strings inline. It results in less code, and is easier to read through and understand.

      Same with tests below.

    6. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
      Show all issues

      Blank line between blocks/statements.

      Same below.

    7. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
      Show all issues

      Docstring summaries are limited to one line.

      Same below.

    8. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
      Show all issues

      The string type is going to be required. Should be unicode.

      Same below.

    9. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
       
      Show all issues

      "Returns" blocks are in the form of:

      Returns:
          type:
          description
      

      So probably unicode for the type.

    10. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
      Show all issues

      We use %-formatting, instead of .format(). It's technically faster.

    11. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
       
       
       
       
      Show all issues

      Blank line between each argument.

    12. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
      Show all issues

      bool

    13. rbtools/commands/tests/test_main.py (Diff revision 4)
       
       
      Show all issues

      bool

    14. 
        
    kpatenio
    Review request changed
    Change Summary:

    Addressed syntax and styling reviews

    Description:
    ~  

    On python 2, running 'rbt --help <command>' and 'rbt <command> --help' are equivalent. However, running

    ~   'rbt <command> --help' does not work on python 3. This happens because byte strings are utilized in the code to see if
    ~   '--help' or '-h' are passed in as arguments. However, '--help' and 'h' are never found because the code is trying to find
    ~   b'--help' or b'-h'. In python 2, b'--help' is equivalent to '--help', but in python 3, this is not the case.

      ~

    On python 2, running rbt --help <command> and rbt <command> --help

      ~ are equivalent. However, running rbt <command> --help does not work
      ~ on python 3. This happens because byte strings are utilized in the
      ~ code to see if --help or -h are passed in as arguments. They are
      + never found because the code is trying to find b'--help' or b'-h'.
      + In python 2, b'--help' is equivalent to '--help', but in python 3,
      + this is not the case.

       
    ~  

    To fix this issue, the byte strings b'--help' and b’-h’ have been changed to python 3 compatible strings (i.e. '--help' and ‘-h’).

    ~   Unit tests have also been added to ensure that all possible positions and cases of the 'help' argument work for rbtools.
    ~   (ex. 'rbt —help <command>', 'rbt <command> -h', 'rbt help <command>', etc. )

      ~

    To fix this issue, the byte strings b'--help' and b’-h’ have been

      ~ changed to python 3 compatible strings (i.e. --help and -h). Unit
      ~ tests have also been added to ensure that all possible positions and
      + cases of the 'help' argument work for rbtools.(ex. rbt —help <command>,
      + rbt <command> -h, rbt help <command>, etc.).

      +
      +

    All tests were run using both python 2.7.17 and python 3.7.6.

    Testing Done:
    ~  

    Testing Done:

    ~   Test case where 'rbt help <command>' is run
    ~   Test case where 'rbt <command> --help' or 'rbt <command> -h' is run
    ~   Test case where 'rbt --help <command>' or 'rbt -h <command>' is run
    ~   Test case where an invalid rbt command is run with 'help'
      ~

    Test cases run on python 2.7.17 and 3.7.6:

      ~ Test case where rbt help <command> is run
      ~ Test case where rbt <command> --help or rbt <command> -h is run
      ~ Test case where rbt --help <command> or rbt -h <command> is run
      ~ Test case where an invalid rbt command is run with help
        Test case where multiple arguments are entered

    Commit:
    382d4bb8d8b664d107f8267aff468ba33986084c
    fbdc829ff4b1d6c0ea256a75a9bc59ea473a2687

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    kpatenio
    kpatenio
    chipx86
    1. 
        
      1. Hi Christian! I updated my code and it looks so much cleaner now. (Deleted about 80 lines of code!). I removed some code relating to error_message (like seen here) since I found them redundant; the assert error messages were enough in my opinion. Thanks for your help!

    2. rbtools/commands/tests/test_main.py (Diff revision 7)
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
      Show all issues

      I've been thinking about these code blocks, which are almost universal in your code. I think what would be better would be to have a single command that takes care of executing these tests. Something like a _check_help_output() method, which takes the command to run and the invalid parameter.

      That would take care of the try/execute/except block, would take care of creating the fail message (you could just roll that logic up into this method), and the assertion.

      The assertion itself can then become an assertIn, rather than an assertTrue(... in ...), which is what it basically is today. That allows us to leverage the standard behavior in unit tests for checking for a needle in a haystack, taking advantage of the enhanced failure information that provides.

      This would simplify the tests greatly. Each test for a command would basically be a call. You could remove the for loops and just call this with each variant (which really helps when debugging test failures -- it's harder when loops are involved).

    3. rbtools/commands/tests/test_main.py (Diff revision 7)
       
       
       
       
      Show all issues

      How about making this one statement?

      Same below.

    4. rbtools/commands/tests/test_main.py (Diff revision 7)
       
       
       
       
       
      Show all issues

      This could be one line now.

      Same with ones below.

    5. rbtools/commands/tests/test_main.py (Diff revision 7)
       
       
       
       
      Show all issues

      Sorry, I wasn't clear in my description above. We'd want a blank line when separating blocks on the same indentation level, but you wouldn't have one between the start of a block and that block's first statement.

      This is fine:

      test_rbt_commands = [
          ...
      ]
      
      for test_command in test_rbt_commands:
          try:
              ...
      
    6. 
        
    kpatenio
    bui1
    1. Ship It!
    2. 
        
    chipx86
    1. 
        
    2. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
      Show all issues

      This can be one statement.

    3. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
       
       
       
       
      Show all issues

      Here, and below, it'll be a lot easier to debug if we're passing in the command directly to _check_help_output. The list doesn't benefit us in any way, since we unrolled the loops, and can actually lead to future copy/paste bugs (if we skip an index or miss an item in the list).

    4. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
      Show all issues

      This should be "Check". We use a form of "Do this, do that" rather than "Does this, does that." in the summary.

    5. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
      Show all issues

      The type should be (bool, optional), since it's not required.

    6. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
      Show all issues

      True should be shown as a literal, using double-backticks.

    7. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
       
       
       
      Show all issues

      Functions that don't return a value shouldn't have a Returns section.

    8. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
       
       
      Show all issues

      This seems fragile. I'm not wild about it. The idea is to have a version of command that didn't have a --help variant in it, right?

      It's best when making a unit test helper function to not have to guess too much about the caller's intention. I think this should simply take another parameter specifying the RBTools subcommand name. subcommand_name should be fine. You can then just pass that into the output strings, and avoid the guessing here.

    9. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
       
       
       
       
      Show all issues

      Rather than defining the variable, you can move the assertIn into those if statements.

    10. rbtools/commands/tests/test_main.py (Diff revision 8)
       
       
       
      Show all issues

      This is only here for the execute command, right? In this case, let's only wrap as much as we need to in the try/except. We should only require the execute to be wrapped.

    11. 
        
    kpatenio
    mike_conley
    1. This looks great. I really appreciate the detailed description / testing don fields, and I love that we're adding tests for this. Thanks!

    2. 
        
    chipx86
    1. Ship It!
    2. 
        
    kpatenio
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to master (0143911)