Skip to content

Conversation

@agatti
Copy link
Contributor

@agatti agatti commented Nov 18, 2025

This PR adds support for optional custom argument type validation to argparse.ArgumentParser, allowing for shorter argument validation code for both simple builtins and complex types, as supported by CPython.

CPython also provides the argparse.FileType class to simplify argument file/directory handling, but that was not added to the MicroPython module. Its behaviour is rather easy to manually replicate if needed anyway.

This change increases the size of the argparse module by 291 bytes, and that's mostly due to the extra error handling and related text strings. Probably those new strings can be shortened, but given that this module isn't usually included in production code, the size increase shouldn't hurt too much I guess.

The unit tests unfortunately do not cover the case of a parameter failing validation, as the module will automatically issue a sys.exit call on error, after printing the relevant exception message. If there are cleaner ways to test that without hijacking sys.exit, let me know so I can update the tests accordingly.

@stinos
Copy link

stinos commented Nov 18, 2025

I'd suggest to make this as much CPython-compatible as possible. It has this particular behavior: https://docs.python.org/3/library/argparse.html#type

If the type keyword is used with the default keyword, the type converter is only applied if the default is a string.

So in CPython the resulting behavior is this:

parser.add_argument("-g", type=CustomArgType(1), default=1)
parser.add_argument("-i", type=CustomArgType(1), default="1")
args = parser.parse_args([])
assert args.g == 1
assert args.i == 2

@agatti agatti force-pushed the argparse-customtypes branch 2 times, most recently from 5c007f4 to 13541ff Compare November 18, 2025 20:15
@agatti
Copy link
Contributor Author

agatti commented Nov 18, 2025

I'd suggest to make this as much CPython-compatible as possible. It has this particular behavior: ...

Thanks for spotting that! I've made it more CPython-compatible, at the expense of an increased footprint by 100 more bytes. The case you mentioned should be handled, along with the exception handling limitations of the custom type parser (ie. let anything that's not ArgumentTypeError, TypeError, and ValueError through and catch the rest).

Right now the main differences between this and CPython should be the lack of type registration, and argparse.FileType not being present. This should cover most usages, I suppose.

@stinos
Copy link

stinos commented Nov 19, 2025

Right now the main differences between this and CPython should be the lack of type registration, and argparse.FileType not being present. This should cover most usages, I suppose.

Yeah should be fine. Also because those would lead to an error immediately, whereas the type/default thingie is harder to discover and could lead to surprising results.

@agatti agatti force-pushed the argparse-customtypes branch 2 times, most recently from 6936a30 to 2859d02 Compare November 21, 2025 06:02
except Exception as e:
if isinstance(e, (ArgumentTypeError, TypeError, ValueError)):
raise _ArgError("invalid value for %s: %s (%s)" % (optname, arg, str(e)))
raise
Copy link
Member

Choose a reason for hiding this comment

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

How about writing this as more idiomatic Python:

except (ArgumentTypeError, TypeError, ValueError) as e:
    raise _ArgError(...)

I think that's equivalent? It also saves 12 bytes in the resulting .mpy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought this syntax wasn't supported by MicroPython, good to know I was wrong all along :)

It is indeed equivalent. Thanks for pointing this out, I've changed the code to reflect that.

@agatti agatti force-pushed the argparse-customtypes branch from 2859d02 to df811c0 Compare December 4, 2025 06:43
This commit adds support for optional custom argument type validation to
argparse.ArgumentParser, allowing for shorter argument validation code
for both simple builtins and complex types.

For example, assuming that a particular command line argument must be an
integer, using "parser.add_argument('-a', type=int)" will make sure that
any value passed to that argument that cannot be converted into an
integer will trigger an argument validation error.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
@dpgeorge dpgeorge force-pushed the argparse-customtypes branch from df811c0 to 7473d1d Compare December 5, 2025 02:53
@dpgeorge dpgeorge merged commit 7473d1d into micropython:master Dec 5, 2025
4 checks passed
@@ -1,4 +1,4 @@
metadata(version="0.4.0")
metadata(version="0.5.0")
Copy link
Member

Choose a reason for hiding this comment

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

I bumped the minor version, because this change is adding a feature.

@agatti agatti deleted the argparse-customtypes branch December 5, 2025 18:14
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.

3 participants