Coder Social home page Coder Social logo

datargs's People

Contributors

brentyi avatar diogo-rossi avatar roee30 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

datargs's Issues

[Feature request] make `aliases` parameters overwrite the default field name in the command line

I understood that the dest parameter of the original add_argument function is not necessary because it is taken from the dataclass field name. But I also understood that the this "field name" is used as the first name_or_flags of the function add_argument, right?

(Please, tell me if I'm wrong)

So, could you make this "field name" be overwritten in the command line, when you use the aliases option?

This would be useful for 2 features:


1. Let dest used inside the script be different from the flags we would like to expose in the command line

Today, we can't reproduce the following:

Example:

from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-u", "--user-name", dest="user")
parser.add_argument("-p", "--file-path", dest="path")
options = parser.parse_args(["-u", "jon", "-p", "file.txt"])
print(options)

Output:

Namespace(user='jon', path='file.txt')

Helper info:

usage: script.py [-h] [-u USER] [-p PATH]

options:
  -h, --help            show this help message and exit
  -u USER, --user-name USER
  -p PATH, --file-path PATH

In summary: inside the script we used user and path but the command line flags are -u,--user-name and -p,--file-path


2. Change the order of flags presented in the Helper info

Today, the last helper info shown above can not be reproduced with the following:

from dataclasses import dataclass
from datargs import arg, parse

@dataclass
class Args:
    user_name: str = arg(aliases=['-u'])
    file_path: str = arg(aliases=['-p'])

options = parse(Args,['-h'])

Because the flags are shown after the field names:

usage: script.py [-h] --user-name USER_NAME --file-path FILE_PATH

options:
  -h, --help            show this help message and exit
  --user-name USER_NAME, -u USER_NAME
  --file-path FILE_PATH, -p FILE_PATH

The only way to reproduce something similar would be using u and p as field names.


So, if you let the "field name" be overwritten by the aliases in the command line (when aliases are used) these 2 features could be added to the library.

BTW. Thank you for this great library!
I was just looking for something like this.

Union operator (`|`) introduced in python 3.10 is not supported.

Since python 3.10, Union[X, Y] is equivalent to X | Y

And the same is for Optional[X] is equivalent to X | None (or Union[X, None])

But the following raises an error

import typing, logging
from datargs import argsclass, arg, parse

@argsclass(description="install package")
class Install:
    package: str = arg(positional=True, help="package to install")

@argsclass(description="show all packages")
class Show:
    verbose: bool = arg(help="show extra info")

@argsclass(description="Pip Install Packages!")
class Pip:
    action: Install | Show
    log: str = None

args = parse(Pip, ["-h"])

Error:

ValueError: __main__.Install | __main__.Show is not callable

Static/dynamic type consistency for optional parameters, nargs?

Hi!

@roee30 do you have any suggestions for how to make static and dynamic types consistent when using nargs or optional parameters with a default value of None?

In particular, I'd like to write something like:

import datargs
from typing import List, Optional

@dataclasses.dataclass(frozen=True)
class CliArgs:
    paths: List[str] = datargs.arg(
        positional=True,
        nargs="+",
        help="List of paths.",
    )
    number: Optional[int] = datargs.arg(default=None)

args = datargs.parse(CliArgs)

# args.paths is a list, args.number is `None` or an integer

But this doesn't work, and I instead have to write:

import datargs

@dataclasses.dataclass(frozen=True)
class CliArgs:
    paths: str = datargs.arg(
        positional=True,
        nargs="+",
        help="List of paths.",
    )
    number: int = datargs.arg(default=None)

args = datargs.parse(CliArgs)

# Dynamically, args.paths is a list, args.number is `None` or an integer
# But type checkers, IDEs, etc will treat args.path as a string, and args.number as always an integer

Thanks!!

The default type is not same as the return type

class Args:
    exclude: typing.Sequence[str] = arg(default=())

args = parse(Args)
print(args.exclude)

# python test.py --exclude a b
# Output: ["a", "b"]

The default argument only accept tuple, but the return type is list.
Expect tuple.

feature request: sub commands aliases

Hi, thanks a lot for this library!

I would like to use sub commands, but have the ability to set the command name;
that's because I usually give the name ActionNameConfig or something like that to the classes holding the configuration/arguments, to better indicate what the class is about.

Is there already the option to give the command a custom name or is it something that needs to be implemented?

thanks again, keep up the good work!

feature request: union of arg classes

Let's say we have two data classes, which both are required. E.g. model config and data config.
Would it be possible to arg-parse a union of both?

Show default value support

argparse can use formatter_class to show default value, hope this lib can supported

parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)

ImportError: cannot import name 'get_origin'

When I run import datargs I get:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/mnt/beegfs/work/baumgaertner/gen-bioasq/.venv/lib64/python3.6/site-packages/datargs/__init__.py", line 1, in <module>
    from .make import parse, arg, make_parser, argsclass
  File "/mnt/beegfs/work/baumgaertner/gen-bioasq/.venv/lib64/python3.6/site-packages/datargs/make.py", line 58, in <module>
    from typing import (
ImportError: cannot import name 'get_origin'

[Feature request] make `aliases` in `arg()` behaves same as in the original `add_argument()` method

The first argument of the original add_argument() method is the name or flags positional argument that can be a series of flags.

The function args() could accept the same format.

I think this can be achieved with this simple modification:

 def arg(
+    *aliases: str,
     positional=False,
     nargs=None,
     const=None,
@@ -569,7 +570,6 @@ def arg(
     choices=None,
     help=None,
     metavar=None,
-    aliases: Sequence[str] = (),
     **kwargs,
 ) -> Any:
     """

What do you think?

[Feature request] Support mutually exclusive parameters

As informed in this section of README.md, the groups of mutually exclusive options are not supported.
I would like to discuss this feature and some solutions.

One idea to support that would be with an additional argument mutually_exclusive_groups in the argsclass decorator:

GroupName = str # type alias
GroupRequired = bool # type alias

def argsclass(
    ...
    mutually_exclusive_groups: dict[GroupName, GroupRequired] = None
    ...
):

It may also be added to the arg function, receiving the name of the group.

Usage:

from datargs import argsclass, arg


@argsclass(mutually_exclusive_groups={"group1": True})
class Args:
    foo: str = arg(mutually_exclusive_group="group1")
    bar: str = arg(mutually_exclusive_group="group1")

Is that feasible?

`attrs` based classes fail with `nargs` option.

Thank you for this useful & compact library! I noticed that the following fails:

from datargs import parse, arg
from dataclasses import dataclass
import attr

@attr.s(auto_attribs=True)
class Settings:
    """Settings"""
    mylist: float = arg(nargs=3)


if __name__ == "__main__":
    args = parse(Settings)
    print(args)

with

$ python3 datargstest.py --mylist 1 2 3
usage: datargstest.py [-h] [--mylist MYLIST]
datargstest.py: error: unrecognized arguments: 2 3

However, if I replace attr.s(auto_attribs=True) with dataclass, it works as expected:

$ python3 datargstest.py --mylist 1 2 3
Settings(mylist=[1.0, 2.0, 3.0])

Optional typing with Enum class

I am having issues using Enum for an optional parameter. It looks like support for optional typing was added in #4, so it may be the Enum class causing the issue.

class Item(enum.Enum):
    car = 0
    truck = 0


@dataclass
class Args:
    item: typing.Optional[Item] = None

Using with optional gives me this error:

error: argument --item: invalid Item value: 'car'

Trying without Optional and a default of None works, but the type checker doesn't like it:

Expression of type "Item | None" cannot be assigned to declared type "Item"

Unable to pass `action` in field.metadata

I am trying to add a verbosity parameter using action="count".

Dataclass example:

@dataclass
class Args:
    """Main program options."""
    verbosity: int = field(
        default=0,
        metadata=dict(
            aliases=["-v"],
            help="Increase logging verbosity",
            action="count",
        ),
    )

I get this error:

Traceback (most recent call last):
  File "/home/nick/git/meetingtool/meetingtool.py", line 285, in <module>
    main()
  File "/home/nick/git/meetingtool/meetingtool.py", line 202, in main
    args = parse(Args)
  File "/home/nick/git/meetingtool/.venv/lib/python3.9/site-packages/datargs/make.py", line 387, in parse
    result = vars(make_parser(cls, parser=parser).parse_args(args))
  File "/home/nick/git/meetingtool/.venv/lib/python3.9/site-packages/datargs/make.py", line 294, in make_parser
    return _make_parser(record_class, parser=parser)
  File "/home/nick/git/meetingtool/.venv/lib/python3.9/site-packages/datargs/make.py", line 335, in _make_parser
    parser.add_argument(*action.args, **action.kwargs)
  File "/home/nick/.asdf/installs/python/3.9.1/lib/python3.9/argparse.py", line 1416, in add_argument
    action = action_class(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'type'

If I comment out action="count", I get:

Args(verbosity=0)

If there's some other way of doing it, I haven't found it.

Support add_argument type parameter

With argparse.add_argument it is possible to convert the argument passed on the command line to a different type. It would be useful to have this functionality using datargs, but does not seem possible (e.g., type is not a supported keyword for the arg utility function).

For example, I have a command line option for setting the log level. I would like the end user to be able to specify the level using its name, a string, but stored in the dataclass as an int so that it can be directly used to setup the logging configuration.

It's certainly possible to do the conversion afterwards, but it would be more convenient to have the transformation done during parsing. Currently, I might reuse the same arguments dataclass in multiple programs, which requires me to apply the type transformation in each program, versus having it done transparently by the parser.

[Edit]
One other thing that doing the conversion client side makes less convenient are choices. Using the type conversion with argparse it is easy to make string choices case insensitive, but doing it client side requires the command line argument to match the case exactly.

pylance (pyrigth) reporting error of returned type of `arg` function

The following snippet returns a error by pylance/pyright (in VSCode) in function arg

from dataclasses import dataclass
from datargs import parse, arg
@dataclass
class Args:
    retries: int = arg(default=3, help="number of retries", aliases=["-r"], metavar="RETRIES")
parse(Args, ["-h"])

image

The message is:

Expression of type "Unknown | _MISSING_TYPE" cannot be assigned to declared type "int"
  Type "Unknown | _MISSING_TYPE" cannot be assigned to type "int"
    "_MISSING_TYPE" is incompatible with "int"

Union operator (`|`) incompatible with __future__.annotations

It appears the parser breaks when using the | operator in conjunction with importing annotations from __futures__. On python 3.12 with datargs installed from main this example errors out:

from __future__ import annotations

from datargs import arg, argsclass, parse


@argsclass
class Eat:
    food: str = arg(positional=True)

    def run(self):
        print("eating", self.food)


@argsclass
class Sleep:
    time: str = arg(positional=True)

    def run(self):
        print("sleeping", self.time)


@argsclass
class App:
    action: Eat | Sleep

    def run(self):
        self.action.run()


if __name__ == "__main__":
    args = parse(App)
    args.run()
Traceback (most recent call last):
...
  File ".../lib/python3.12/site-packages/datargs/make.py", line 481, in parse
    result = vars(make_parser(cls, parser=parser).parse_args(args))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../lib/python3.12/site-packages/datargs/make.py", line 372, in make_parser
    return _make_parser(record_class, parser=parser)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../lib/python3.12/site-packages/datargs/make.py", line 420, in _make_parser
    parser.add_argument(*action.args, **action.kwargs)
  File ".../lib/python3.12/argparse.py", line 1477, in add_argument
    raise ValueError('%r is not callable' % (type_func,))
ValueError: 'Eat | Sleep' is not callable

It works just fine if you don't import from __futures__.

PyPI release?

Hi @roee30 !

Would you be able to do a release on PyPI with support for Literal types?

I just did a code release for some research code that uses datargs, and it'd make dependency tracking a bit easier on my end (cc brentyi/dfgo#1).

A glance at #16 would also be appreciated.

Thanks :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.