Repository: omni-us/jsonargparse

Issue:
Show default values for positional arguments in `--help`
<!-- If you like this project, please ⭐ star it https://github.com/omni-us/jsonargparse/ -->

## 🚀 Feature request

Running `--help` in the CLI should print the default values of positional arguments.

### Motivation

User should be able to see all the default values that will be passed.

In the following example
```python
from jsonargparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('foo', default='a', type=str)
parser.add_argument('--bar', default=2, type=int)
cfg = parser.parse_args()
```
only the default value of the optional `bar` argument is shown:

```
usage: parser.py [-h] [--bar BAR] foo

positional arguments:
  foo         (required, type: str)

options:
  -h, --help  Show this help message and exit.
  --bar BAR   (type: int, default: 2)
```

### Pitch

```
usage: parser.py [-h] [--bar BAR] foo

positional arguments:
  foo         (required, type: str, default: a)

options:
  -h, --help  Show this help message and exit.
  --bar BAR   (type: int, default: 2)
```

### Alternatives

<!-- Any alternative solutions or features you've considered, if any. -->

Hints from issue comments:
@mauvilsa: Thanks for proposing a feature!

This request is somewhat unexpected. Positionals are supposed to be required, as in, the user must provide a value. I tried giving a default to a positional with `argparse` and surprising it doesn't complain. Though, the default is useless, because the positional must always be provided, which means the default is always replaced.

Currently in `jsonargparse` not providing the positional succeeds, though, this is **not** an intended behavior. Just because right now this doesn't fail, it doesn't mean that after an internal change (e.g. to fix #427) the same behavior would be keep. I am tempted to fail adding the argument if a positional gets a default.

Anyway, the motivation is not clear. Why is it that you want to have a default for a positional? Is it just that you want the argument to be optional, but from command line be positional, not requiring a `--key`? In a sense this could be the same as #690.@adosar: Hi @mauvilsa ,

My motivation for showing the default was because it proceeded without failing if I don't specify a value for the positional argument. **Since this is not the intended behavior** and will be changed there is no reason for this feature.

Solution patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index bdf5ac7c..56e71b7c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -25,6 +25,8 @@ Changed
 - ``validate`` now checks values before required so that errors related to wrong
   level in a config are easier to understand (`#681
   <https://github.com/omni-us/jsonargparse/pull/681>`__).
+- ``add_argument`` of a positional and providing ``default`` now raises an error
+  (`#694 <https://github.com/omni-us/jsonargparse/pull/694>`__).
 
 Fixed
 ^^^^^
diff --git a/jsonargparse/_core.py b/jsonargparse/_core.py
index 1a950a62..dd247d4a 100644
--- a/jsonargparse/_core.py
+++ b/jsonargparse/_core.py
@@ -147,6 +147,8 @@ def add_argument(self, *args, enable_path: bool = False, **kwargs):
         ActionJsonnet._check_ext_vars_action(parser, action)
         if is_meta_key(action.dest):
             raise ValueError(f'Argument with destination name "{action.dest}" not allowed.')
+        if action.option_strings == [] and "default" in kwargs:
+            raise ValueError("Positional arguments not allowed to have a default value.")
         if action.help is None:
             action.help = empty_help
         if action.required:


Test patch:
diff --git a/jsonargparse_tests/test_core.py b/jsonargparse_tests/test_core.py
index 23acf328..2483445a 100644
--- a/jsonargparse_tests/test_core.py
+++ b/jsonargparse_tests/test_core.py
@@ -47,6 +47,11 @@
 )
 
 
+def test_add_argument_positional_with_default(parser):
+    with pytest.raises(ValueError, match="Positional arguments not allowed to have a default value"):
+        parser.add_argument("pos", default="abc")
+
+
 def test_parse_args_simple(parser):
     parser.add_argument("--op", type=int)
     assert parser.parse_args(["--op=1"]) == Namespace(op=1)