Declarative CLIs with argparse and dataclasses

Overview

argparse_dataclass

Declarative CLIs with argparse and dataclasses.

https://travis-ci.org/mivade/argparse_dataclass.svg?branch=master

PyPI

Features

Features marked with a ✓ are currently implemented; features marked with a ⊘ are not yet implemented.

  • [✓] Positional arguments
  • [✓] Boolean flags
  • [✓] Integer, string, float, and other simple types as arguments
  • [✓] Default values
  • [✓] Arguments with a finite set of choices
  • [⊘] Subcommands
  • [⊘] Mutually exclusive groups

Examples

Using dataclass decorator

>>> from argparse_dataclass import dataclass
>>> @dataclass
... class Options:
...     x: int = 42
...     y: bool = False
...
>>> print(Options.parse_args(['--y']))
Options(x=42, y=True)

A simple parser with flags:

>>> from dataclasses import dataclass
>>> from argparse_dataclass import ArgumentParser
>>> @dataclass
... class Options:
...     verbose: bool
...     other_flag: bool
...
>>> parser = ArgumentParser(Options)
>>> print(parser.parse_args([]))
Options(verbose=False, other_flag=False)
>>> print(parser.parse_args(["--verbose", "--other-flag"]))
Options(verbose=True, other_flag=True)

Using defaults:

>>> from dataclasses import dataclass, field
>>> from argparse_dataclass import ArgumentParser
>>> @dataclass
... class Options:
...     x: int = 1
...     y: int = field(default=2)
...     z: float = field(default_factory=lambda: 3.14)
...
>>> parser = ArgumentParser(Options)
>>> print(parser.parse_args([]))
Options(x=1, y=2, z=3.14)

Enabling choices for an option:

>>> from dataclasses import dataclass, field
>>> from argparse_dataclass import ArgumentParser
>>> @dataclass
... class Options:
...     small_integer: int = field(metadata=dict(choices=[1, 2, 3]))
...
>>> parser = ArgumentParser(Options)
>>> print(parser.parse_args(["--small-integer", "3"]))
Options(small_integer=3)

Using different flag names and positional arguments:

>> parser = ArgumentParser(Options) >>> print(parser.parse_args(["-x", "0", "positional"])) Options(x=0, positional='positional') >>> print(parser.parse_args(["--long-name", 0, "positional"])) Options(x=0, positional='positional') ">
>>> from dataclasses import dataclass, field
>>> from argparse_dataclass import ArgumentParser
>>> @dataclass
... class Options:
...     x: int = field(metadata=dict(args=["-x", "--long-name"]))
...     positional: str = field(metadata=dict(args=["positional"]))
...
>>> parser = ArgumentParser(Options)
>>> print(parser.parse_args(["-x", "0", "positional"]))
Options(x=0, positional='positional')
>>> print(parser.parse_args(["--long-name", 0, "positional"]))
Options(x=0, positional='positional')

Using a custom type converter:

>>> from dataclasses import dataclass, field
>>> from argparse_dataclass import ArgumentParser
>>> @dataclass
... class Options:
...     name: str = field(metadata=dict(type=str.title))
...
>>> parser = ArgumentParser(Options)
>>> print(parser.parse_args(["--name", "john doe"]))
Options(name='John Doe')

License

MIT License

Copyright (c) 2021 Michael V. DePalatis and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Comments
  • Moving _add_dataclass_options() to be a standalone function

    Moving _add_dataclass_options() to be a standalone function

    This might be a more controversial change. 😄 I was looking at the new dataclass decorator overload that was implemented. It is a nice concept except that type hinting does not work with it. Since type hinting is the main benefit over normal argparse (In my opinion) I came up with this other approach.

    Not sure how much approach conflicts with your vision of what you want but it seems to offer a similar experience as the dataclass approach while maintaining type hints.

    opened by jcal-15 4
  • Allow type overriding

    Allow type overriding

    This PR allows the user to override the type argument passed to add_argument. This is useful when a custom function needs to be used, but is not appropriate as a type. For example, from the argparse documentation:

    def hyphenated(string):
        return '-'.join([word[:4] for word in string.casefold().split()])
    
    parser = argparse.ArgumentParser()
    _ = parser.add_argument('short_title', type=hyphenated)
    parser.parse_args(['"The Tale of Two Cities"'])
    Namespace(short_title='"the-tale-of-two-citi')
    

    With this PR, one could define an argument like:

    @dataclass
    class Options:
        short_title: str = field(metadata=dict(type=hyphenated))
    

    And thus the following will work correctly:

    parser = argparse_dataclass.ArgumentParser(Options)
    parser.parse_args(['--short-title', '"The Tale of Two Cities"'])
    

    Which results in Options(short_title='"the-tale-of-two-citi')

    opened by rafi-cohen 4
  • More tests

    More tests

    Some more testing:

    • tests/test_argumentparser.py has been updated so it has the same tests as tests/test_functional.py.
    • in tests/test_functional.py, Fixed the name of the test class.
    opened by jcal-15 3
  • Add custom `dataclass` decorator

    Add custom `dataclass` decorator

    This is like the standard dataclass decorator but will also optionally (on by default) add a parse_args class method to the generated class. This would allow us to do shortcuts like:

    @dataclass
    class Parameters:
        x: int
        y: int
    
    params = Parameters.parse_args()
    
    enhancement 
    opened by mivade 2
  • Support positional arguments

    Support positional arguments

    Since I am in the happy position of building a school project and not being able to depend on click for command-line arguments, I decided to see if it was possible to combine dataclasses and argparse. Apparently your library (which I would have to rewrite anyway) doesn't make it possible to provide positional arguments. Would that be possible through another metadata flag?

    enhancement 
    opened by nyanpasu64 2
  • Needs explicit type in metadata for Optional[type] fields

    Needs explicit type in metadata for Optional[type] fields

    The following class fails to parse:

    @dataclass
    class IndexOptsMinimal:
        index: Optional[int] = None
    

    The following workaround is required:

    @dataclass
    class IndexOptsWorkaround:
        index: Optional[int] = field(metadata=dict(type=int), default=None)
    

    Using optional arguments that are allowed to be None is such a common pattern that it deserves to supported implicitly.

    opened by cdleonard 1
  • Locking some ArgumentParser functions after initialization

    Locking some ArgumentParser functions after initialization

    I am not really sure about this change anymore.

    Originally, I thought it would be helpful let a user know that they were about to do something that would break the parser, but realistically most people will understand that adding arguments after the parser has been created does not make sense.

    I wanted to see what you thought before putting more time into it.


    Sorry so many pull request in a short time. I have been taking a larger set of changes I had lying around and breaking them out by feature.

    I only have one left. It is a documentation update. I am putting it off for last. 😄

    opened by jcal-15 1
  • Updates for boolean fields

    Updates for boolean fields

    • adding support for default values of True
    • adding support to require a user to specify the value

    Also, Adding Python 3.10 to the list of support Python versions and adding testing.

    opened by jcal-15 1
  •  Updating type hints so that ArgumentParser.parse_args() returns the correct hint

    Updating type hints so that ArgumentParser.parse_args() returns the correct hint

    These are pretty small changes. They have no functional changes. This allow VSCode (and probably other IDEs) to detect the dataclass type that is being returned by parse_args().

    --- BEFORE -- Before_B1

    Before_B2

    -- AFTER -- After_B1

    After_B2

    opened by jcal-15 1
  • dataclass decorator not working as expected

    dataclass decorator not working as expected

    Hi,

    I seem to be running into a couple of issues when using the decorator that was recently added in #9:

    1. It does not work with optional keyword arguments:
    from argparse_dataclass import dataclass
    
    @dataclass(frozen=True)
    class Opt:
        x: int = 42
        y: bool = False
    

    Error: TypeError: cls must be a dataclass It seems that cls here is actually real_dataclass.wrap.

    1. It does not behave like a regular dataclass:
    from argparse_dataclass import dataclass
    
    @dataclass
    class Opt:
        x: int = 42
        y: bool = False
    
    opt = Opt()
    print(opt.x)
    

    Error: AttributeError: 'Inner' object has no attribute 'x' Looks like Opt was "replaced" with Inner, which is defined in argparse_dataclass.dataclass.

    opened by rafi-cohen 1
  • Support nargs

    Support nargs

    This PR add support for setting the nargs argument passed to add_argument. An example usage of this is as follows:

    from dataclasses import dataclass, field
    from typing import List
    from argparse_dataclass import ArgumentParser
    
    @dataclass
    class Options:
        values: List[int] = field(metadata=dict(type=int, nargs='*'))
    
    parser = ArgumentParser(Options)
    parser.parse_args('--values 1 2 3'.split())
    

    This example results in Options(values=[1, 2, 3])

    Note that this PR depends on #11 This is because when nargs is used, an appropriate field type would be a list. However, then ArgumentParser will parse each element as a list, which is likely an undesired behaviour. Therefore, when nargs is specified, it expects type to be explicitly specified as well.

    opened by rafi-cohen 1
  • Do you want to publish it in Pypi ?

    Do you want to publish it in Pypi ?

    I don't know if this is the right place to write, but I don't know where else write it, anyway.... Are you intending to publish the lib again on pypi? I found this library very useful and it could be worth!

    Thanks in advance

    opened by TheSmike 2
  • Option to keep underscores in arguments names

    Option to keep underscores in arguments names

    Thank you for this library.

    I would like to have an option that would allow to keep the underscores in the argument names instead of replacing them by minus signs. I can prepare a PR for that change if that is a feature you are happy to integrate.

    opened by martinResearch 1
  • Is nested dataclass supported?

    Is nested dataclass supported?

    from argparse_dataclass import dataclass
    from argparse_dataclass import ArgumentParser
    
    @dataclass
    class SubOption:
        a: int = 1
        b: int = 2
    
    @dataclass
    class Options:
        x: int = 42
        y: bool = False
        sub: SubOption = None
    
    parser = ArgumentParser(Options)
    x = parser.parse_args(['--sub', '1'])
    print(x)
    

    for example, how can I parse args to get a value for sub?

    opened by sleepwalker2017 3
  • Default help formatter is not working

    Default help formatter is not working

    example code: from dataclasses import dataclass, field from argparse_dataclass import ArgumentParser from argparse import ArgumentDefaultsHelpFormatter

    @dataclass
    class FineParams:
        batch_size: int = field(default=300) 
    
    parser = ArgumentParser(GCPFineParams, formatter_class=ArgumentDefaultsHelpFormatter)
    params: FineParams = parser.parse_args()
    

    resulting non-defaults help printout.

    diving into argparse code (argparse.py, ver 1.1, line 1861) shone that the ArgumentParser's _actions class members, batch_size action is stored as _StoreAction(option_strings=['--batch-size'], dest='batch_size', nargs=None, const=None, default=<dataclasses._MISSING_TYPE object at 0x7f8465889960>, type=<class 'int'>, choices=None, required=False, help=None, metavar=None)

    opened by assafge 0
Owner
Mike DePalatis
Mike DePalatis
Create argparse subcommands with decorators.

python-argparse-subdec This is a very simple Python package that allows one to create argparse's subcommands via function decorators. Usage Create a S

Gustavo José de Sousa 7 Oct 21, 2022
A Python command-line utility for validating that the outputs of a given Declarative Form Azure Portal UI JSON template map to the input parameters of a given ARM Deployment Template JSON template

A Python command-line utility for validating that the outputs of a given Declarative Form Azure Portal UI JSON template map to the input parameters of a given ARM Deployment Template JSON template

Glenn Musa 1 Feb 3, 2022
A CLI tool for searching and watching videos on youtube with no spyware and MPV and yt-dlp

A CLI tool for searching and watching videos on youtube with no spyware and MPV and yt-dlp

TruncatedDinosour 3 Feb 22, 2022
Python wrapper and CLI utility to render LaTeX markup and equations as SVG using dvisvgm and svgo.

latex2svg Python wrapper and CLI utility to render LaTeX markup and equations as SVG using dvisvgm and svgo. Based on the original work by Tino Wagner

Matthias C. Hormann 4 Feb 18, 2022
Management commands to help backup and restore your project database and media files

Django Database Backup This Django application provides management commands to help backup and restore your project database and media files with vari

null 687 Jan 4, 2023
A python script that enables a raspberry pi sd card through the CLI and automates the process of configuring network details and ssh.

This project is one script (wpa_helper.py) written in python that will allow for the user to automate the proccess of setting up a new boot disk and configuring ssh and network settings for the pi

Theo Kirby 6 Jun 24, 2021
liquidctl – liquid cooler control Cross-platform tool and drivers for liquid coolers and other devices

Cross-platform CLI and Python drivers for AIO liquid coolers and other devices

null 1.7k Jan 8, 2023
Sink is a CLI tool that allows users to synchronize their local folders to their Google Drives. It is similar to the Git CLI and allows fast and reliable syncs with the drive.

Sink is a CLI synchronisation tool that enables a user to synchronise local system files and folders with their Google Drives. It follows a git C

Yash Thakre 16 May 29, 2022
git-partial-submodule is a command-line script for setting up and working with submodules while enabling them to use git's partial clone and sparse checkout features.

Partial Submodules for Git git-partial-submodule is a command-line script for setting up and working with submodules while enabling them to use git's

Nathan Reed 15 Sep 22, 2022
Run an FFmpeg command and see the percentage progress and ETA.

Run an FFmpeg command and see the percentage progress and ETA.

null 25 Dec 22, 2022
pyNPS - A cli Linux and Windows Nopaystation client made with python 3 and wget

Currently, all the work is being done inside the refactoring branch. pyNPS - A cli Linux and Windows Nopaystation client made with python 3 and wget P

Everton Correia 45 Dec 11, 2022
A lightweight Python module and command-line tool for generating NATO APP-6(D) compliant military symbols from both ID codes and natural language names

Python military symbols This is a lightweight Python module, including a command-line script, to generate NATO APP-6(D) compliant military symbol icon

Nick Royer 5 Dec 27, 2022
Collection of useful command line utilities and snippets to help you organise your workspace and improve workflow.

Collection of useful command line utilities and snippets to help you organise your workspace and improve workflow.

Dominik Tarnowski 3 Dec 26, 2021
WebApp Maker make web apps (Duh). It is open source and make with python and shell.

WebApp Maker make web apps (Duh). It is open source and make with python and shell. This app can take any website and turn it into an app. I highly recommend turning these few websites into webapps: - Krunker.io (Fps Game) - play.fancade.com (Minigame Arcade) - Your Own Website If You Have One Apart from that enjoy my app By 220735540 (a.k.a RP400)

null 2 Jan 9, 2022
Standalone script written in Python 3 for generating Reverse Shell one liner snippets and handles the communication between target and client using custom Netcat binaries

Standalone script written in Python 3 for generating Reverse Shell one liner snippets and handles the communication between target and client using custom Netcat binaries. It automates the boring stuff like URL encoding the command and setting up a listener.

Yash Bhardwaj 3 Sep 27, 2022
Python command line tool and python engine to label table fields and fields in data files.

Python command line tool and python engine to label table fields and fields in data files. It could help to find meaningful data in your tables and data files or to find Personal identifable information (PII).

APICrafter 22 Dec 5, 2022
A command line tool to hide and reveal information inside images (works for both PNGs and JPGs)

Imgrerite A command line tool to hide and reveal information inside images (works for both PNGs and JPGs) Dependencies Python 3 Git Most of the Linux

Jigyasu 10 Jul 27, 2022
gget is a free and open-source command-line tool and Python package that enables efficient querying of genomic databases.

gget is a free and open-source command-line tool and Python package that enables efficient querying of genomic databases. gget consists of a collection of separate but interoperable modules, each designed to facilitate one type of database querying in a single line of code.

Pachter Lab 570 Dec 29, 2022