Skip to content

Add Positional Feature#176

Merged
martinjm97 merged 16 commits into
swansonk14:mainfrom
Daraan:add-positional
Mar 28, 2026
Merged

Add Positional Feature#176
martinjm97 merged 16 commits into
swansonk14:mainfrom
Daraan:add-positional

Conversation

@Daraan

@Daraan Daraan commented Dec 28, 2025

Copy link
Copy Markdown
Contributor

Resolves: #169

To also work for pydantic needs: #174, then also resolves: #165 (thats why latest tests) fail

Supports

from tap import Tap, Positional

class MyTap(Tap):
    positional_arg: Positional[int]
    positional_default: Positional[int] = 2
    positional_args: Positional[list[str]]

@codecov-commenter

codecov-commenter commented Dec 28, 2025

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.38%. Comparing base (2215ef5) to head (7199f92).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #176      +/-   ##
==========================================
+ Coverage   94.18%   94.38%   +0.20%     
==========================================
  Files           4        4              
  Lines         757      784      +27     
==========================================
+ Hits          713      740      +27     
  Misses         44       44              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/tap/tap.py
Comment on lines -186 to 188
kwargs["help"] += " " + self.class_variables[variable]["comment"]
if variable in self.class_variables and (comment := self.class_variables[variable]["comment"]):
kwargs["help"] += " " + comment

@Daraan Daraan Dec 29, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This avoids just an additional untrimmed space at the end of the help message if there is none; i.e. "(int, required) " -> "(int, required)"

@Daraan

Daraan commented Dec 30, 2025

Copy link
Copy Markdown
Contributor Author

Ready for review :)

@martinjm97

Copy link
Copy Markdown
Collaborator

Thank you! This looks great! Sorry for the delay, we'll do our best to review this sizable PR soon.

--JK

@Daraan

Daraan commented Feb 28, 2026

Copy link
Copy Markdown
Contributor Author

Tell me if you think something needs a redesign.

@swansonk14 swansonk14 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Daraan,

Thank you so much for this PR! Overall it looks great! One issue we noticed is a mismatch in behavior between argparse and Tap in the way that the order of positionals with defaults is handled. We will work on fixing it in the next few hours. We also left a few other comments regarding additional tests and documentation that we will work on.

--JK

Comment thread tests/test_actions.py
Comment on lines +271 to +274
if PositionalOptional is not PositionalOptionalOrder2:
args2 = tapped2.parse_args(["custom"])
self.assertEqual(args2.arg, "custom")
self.assertEqual(args2.barg, 1)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We agree that this is correct behavior for PositionalOptionalOrder1, but we think that this is incorrect for PositionalOptionalOrder3. In PositionalOptionalOrder3, since barg is the first positional, the input "custom" should map to barg even though barg has a default value, which would then lead to a crash. This happens in argparse as seen in the code below.

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("barg", type=int, default=1)
parser.add_argument("arg", type=str)

args = parser.parse_args(["custom"])

which produces the error

usage: main.py [-h] barg arg
main.py: error: argument barg: invalid int value: 'custom'

We would suggest changing this test so that PositionalOptionalOrder1 runs this code but PositionalOptionalOrder3 expects a SystemExit.

Comment thread tests/test_tapify.py
Comment on lines +1336 to +1337
def greet(name: Positional[str]):
return f"Hello, {name}!"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are great, but it might be worth adding at least one more complex test with multiple positional and non-positional arguments.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be good to include a test that passes an argument to the function directly (via **func_kwargs in tapify) rather than through the CLI. For example,

def greet(name: Positional[str], age: int):
    return f"Hello, {name} ({age})!"


output = tapify(greet, command_line_args=["Alice"], age=32)

Comment thread README.md
class MyTap(Tap):
required_arg: str
default_arg: str = 'default value'
positional_arg: Positional[int]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Show example usage.

"""main.py"""
from tap import Tap, Positional

class MyTap(Tap):
    required_arg: str
    default_arg: str = 'default value'
    positional_arg: Positional[int]

args = MyTap().parse_args()
print(f"positional = {args.positional_arg}, required = {args.required_arg}, default = {args.default_arg}")

For example, running

>>> python main.py 42 --required_arg "I'm required"

would produce

positional = 42, required = "I'm required", default = "default value"

@martinjm97

Copy link
Copy Markdown
Collaborator

We believe we have the few minor fixes completed. We'll merge and then apply those fixes. Thank you again and sorry for the delay on the merge!

--JK

@martinjm97 martinjm97 merged commit c995af1 into swansonk14:main Mar 28, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Positional argument with default value Feature request: Positional arguments from pydantic model

4 participants