Repository: fniessink/next-action

Issue:
next-action --due @work gives error message
This is because `--due` has an optional argument. Mention in docs that either `next-action @work --due` or `next-action --due -- @work` works.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb96b4e..ee37b1d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 
 ### Fixed
 
+- Mention how to deal with options with optional arguments followed by positional arguments in the help information and README. Closes #100.
 - Short options immediately followed by a value weren't parsed correctly. Fixes #84.
 
 ## [0.16.1] - 2018-06-04
diff --git a/README.md b/README.md
index f0a5712..f225b7f 100644
--- a/README.md
+++ b/README.md
@@ -21,8 +21,9 @@ Don't know what *Todo.txt* is? See <https://github.com/todotxt/todo.txt> for the
 - [Usage](#usage)
   - [Limiting the tasks from which next actions are selected](#limiting-the-tasks-from-which-next-actions-are-selected)
   - [Showing more than one next action](#showing-more-than-one-next-action)
-  - [Output options](#output-options)
+  - [Styling the output](#styling-the-output)
   - [Configuring *Next-action*](#configuring-next-action)
+  - [Option details](#option-details)
 - [Recent changes](#recent-changes)
 - [Develop](#develop)
 
@@ -41,7 +42,7 @@ Don't know what *Todo.txt* is? See <https://github.com/todotxt/todo.txt> for the
 ```console
 $ next-action --help
 usage: next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] [-r <ref>] [-s [<style>]] [-a
-| -n <number>] [-d [<due date>] | -o] [-p [<priority>]] [<context|project> ...]
+| -n <number>] [-d [<due date>] | -o] [-p [<priority>]] [--] [<context|project> ...]
 
 Show the next action in your todo.txt. The next action is selected from the tasks in the todo.txt file based
 on task properties such as priority, due date, and creation date. Limit the tasks from which the next action
@@ -90,6 +91,9 @@ limit the tasks from which the next actions are selected:
                         of at least one of the projects
   -@<context> ...       contexts the next action must not have
   -+<project> ...       projects the next action must not be part of
+
+Use -- to separate options with optional arguments from contexts and projects, in order to handle cases
+where a context or project is mistaken for an argument to an option.
 ```
 
 Assuming your todo.txt file is your home folder, running *Next-action* without arguments will show the next action you should do. Given this [todo.txt](https://raw.githubusercontent.com/fniessink/next-action/master/docs/todo.txt), calling mom would be the next action:
@@ -192,7 +196,7 @@ $ next-action --all @store
 
 Note again that completed tasks and task with a future creation date are never shown since they can't be a next action.
 
-### Output options
+### Styling the output
 
 By default, *Next-action* references the todo.txt file from which actions were read if you read tasks from multiple todo.txt files. The `--reference` option controls this:
 
@@ -298,12 +302,39 @@ style: colorful
 
 Run `next-action --help` to see the list of possible styles.
 
-#### Precedence of options
+### Option details
+
+#### Precedence
 
 Options in the configuration file override the default options. Command-line options in turn override options in the configuration file.
 
 If you have a configuration file with default options that you occasionally want to ignore, you can skip reading the configuration file entirely with the `--no-config-file` option.
 
+#### Optional arguments followed by positional arguments
+
+When you use an option that takes an optional argument, but have it followed by a positional argument, *Next-action* will interpret the positional argument as the argument to the option and complain, e.g.:
+
+```console
+$ next-action --due @home
+usage: next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] [-r <ref>] [-s [<style>]] [-a
+| -n <number>] [-d [<due date>] | -o] [-p [<priority>]] [--] [<context|project> ...]
+next-action: error: argument -d/--due: invalid date: @home
+```
+
+There's two ways to help *Next-action* figure out what you mean. Either reverse the order of the arguments:
+
+```console
+$ next-action @home --due
+(K) Pay July invoice @home due:2018-07-28
+```
+
+Or use `--` to separate the option from the positional argument(s):
+
+```console
+$ next-action --due -- @home
+(K) Pay July invoice @home due:2018-07-28
+```
+
 ## Recent changes
 
 See the [change log](https://github.com/fniessink/next-action/blob/master/CHANGELOG.md).
@@ -316,9 +347,9 @@ To run the unit tests:
 
 ```console
 $ python -m unittest
-...................................................................................................................................................................
+....................................................................................................................................................................
 ----------------------------------------------------------------------
-Ran 163 tests in 0.614s
+Ran 164 tests in 0.592s
 
 OK
 ```
diff --git a/docs/update_readme.py b/docs/update_readme.py
index 6d5d826..0c6a123 100644
--- a/docs/update_readme.py
+++ b/docs/update_readme.py
@@ -9,9 +9,10 @@ def do_command(line):
     """ Run the command on the line and return its stdout and stderr. """
     command = shlex.split(line[2:])
     if command[0] == "next-action" and "--write-config-file" not in command:
-        command.extend(["--config", "docs/.next-action.cfg"])
+        command.insert(1, "--config")
+        command.insert(2, "docs/.next-action.cfg")
     command_output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                                    check=True, universal_newlines=True)
+                                    universal_newlines=True)
     return command_output.stdout.strip(), command_output.stderr.strip()
 
 
diff --git a/next_action/arguments/parser.py b/next_action/arguments/parser.py
index 0d7a4b1..2e2e9e2 100644
--- a/next_action/arguments/parser.py
+++ b/next_action/arguments/parser.py
@@ -20,14 +20,16 @@ class NextActionArgumentParser(argparse.ArgumentParser):
 
     def __init__(self) -> None:
         super().__init__(
+            usage=textwrap.fill("next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] "
+                                "[-r <ref>] [-s [<style>]] [-a | -n <number>] [-d [<due date>] | -o] "
+                                "[-p [<priority>]] [--] [<context|project> ...]",
+                                width=shutil.get_terminal_size().columns - len("usage: ")),
             description="Show the next action in your todo.txt. The next action is selected from the tasks in the "
                         "todo.txt file based on task properties such as priority, due date, and creation date. Limit "
                         "the tasks from which the next action is selected by specifying contexts the tasks must have "
                         "and/or projects the tasks must belong to.",
-            usage=textwrap.fill("next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] "
-                                "[-r <ref>] [-s [<style>]] [-a | -n <number>] [-d [<due date>] | -o] "
-                                "[-p [<priority>]] [<context|project> ...]",
-                                width=shutil.get_terminal_size().columns - len("usage: ")))
+            epilog="Use -- to separate options with optional arguments from contexts and projects, in order to handle "
+                   "cases where a context or project is mistaken for an argument to an option.")
         self.__default_filenames = ["~/todo.txt"]
         self.add_optional_arguments()
         self.add_filter_arguments()


Test patch:
diff --git a/tests/unittests/arguments/test_parser.py b/tests/unittests/arguments/test_parser.py
index e0867ee..4e6a5ec 100644
--- a/tests/unittests/arguments/test_parser.py
+++ b/tests/unittests/arguments/test_parser.py
@@ -12,7 +12,7 @@ from next_action.arguments import config, parse_arguments
 
 USAGE_MESSAGE = textwrap.fill(
     "usage: next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] [-r <ref>] [-s [<style>]] "
-    "[-a | -n <number>] [-d [<due date>] | -o] [-p [<priority>]] [<context|project> ...]", 120) + "\n"
+    "[-a | -n <number>] [-d [<due date>] | -o] [-p [<priority>]] [--] [<context|project> ...]", 120) + "\n"
 
 
 class ParserTestCase(unittest.TestCase):
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
index ec2d8cc..8c8978b 100644
--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -66,7 +66,7 @@ class CLITest(unittest.TestCase):
         self.assertRaises(SystemExit, next_action)
         self.assertEqual(call("""\
 usage: next-action [-h] [--version] [-c [<config.cfg>] | -w] [-f <todo.txt> ...] [-r <ref>] [-s [<style>]] [-a | -n
-<number>] [-d [<due date>] | -o] [-p [<priority>]] [<context|project> ...]
+<number>] [-d [<due date>] | -o] [-p [<priority>]] [--] [<context|project> ...]
 
 Show the next action in your todo.txt. The next action is selected from the tasks in the todo.txt file based on task
 properties such as priority, due date, and creation date. Limit the tasks from which the next action is selected by
@@ -115,6 +115,9 @@ limit the tasks from which the next actions are selected:
                         one of the projects
   -@<context> ...       contexts the next action must not have
   -+<project> ...       projects the next action must not be part of
+
+Use -- to separate options with optional arguments from contexts and projects, in order to handle cases where a
+context or project is mistaken for an argument to an option.
 """),
                          mock_stdout_write.call_args_list[0])