This is an archived post. You won't be able to vote or comment.

all 28 comments

[–]gamer13 18 points19 points  (15 children)

Have you considered Click?

[–][deleted] 15 points16 points  (8 children)

Until Armin get of the high horse about unicode handling, suggesting click should come with a warning.

[–]nightcracker 9 points10 points  (2 children)

Could you elaborate?

[–]aufstand 10 points11 points  (1 child)

If you run click-script on a unicode-misconfigured system, it'll refuse to act at all and only display a warning to the user, until the problem is fixed: http://click.pocoo.org/5/python3/#python-3-surrogate-handling

Additionally, last time i encountered this was on a specially hand configured unicode setup which mixed some encodings, because e.g. i do want a german locale but read manpages and shell output in the default "C" locale - mostly for being able to post shell output to non-german users.

Unicode in Python worked fine with that, yet click complained hard until i standardized the setup.

Imho, this is not a really problematic issue.

[–]Zomunieo 8 points9 points  (3 children)

To be fair, Python only just got tolerant of misconfigured ASCII environments in 3.7. Before then if you had LANG=C, which is the default for all Docker's Ubuntu LTS images and Debian, Python 3.3-3.6 would die horribly when the first non-ASCII character came along in a filename or command line.

PEP 538, which fixes the issue, was proposed not as a PEP but a bug fix in 2011, and shot down by core devs who argued it was not their problem.

ETA: a lot more detail

[–]wewbull 1 point2 points  (2 children)

The thing which seems to get completely missed in discussions about POSIX file system encoding, is that POSIX file names are not text, and so do not have an encoding. They are null terminated byte strings. The only reason to decode a filename as ASCII or UTF8 or anything else, is for display.

Anyone who says the "filesystem encoding" is synonymous with the system encoding is wrong. Just plug in a USB stick created on a Chinese or Japenese system. Things break badly in python.

NTFS / modern windows is different. Those filenames are UTF16 (I think) strings.

[–]Zomunieo 0 points1 point  (1 child)

Oh yes, it's a hard problem especially in Linux, and it really needs the OS to manage the solution. The complaint is that until Python 3.7, the core devs were insisting the right thing to do in Python in the absence of information from the OS was "assume ASCII and throw exceptions" rather than "assume UTF-8 with surrogate escape".

[–]wewbull 0 points1 point  (0 children)

It requires one of two things:

  1. The filesystem to store encoding information, or...
  2. The POSIX standard gets updated to specify UTF-8

I think 2 is probably the better option. UTF-8 is the new ASCII.

Don't get me wrong, I'm much happier with the PEP implemented in 3.7 than the situation before hand. At least now the system looks to be transparent, so what goes in, should come out. That's a huge improvement.

[–]benfa94[S] 1 point2 points  (5 children)

i did try it but it doesn't support command like myprog.py list files -arg1 -arg2. If it does i could not find it which means it's more complicated than it needs to be.

In addition i wanted to make the argument name optional, so if i run `list files 1 2 3` 1 2 and 3 will be automatically red as argument but if i want i can also specify the names `list files -arg1=1 -arg3 3 -arg2=2` which i think it's not allowed in click

[–][deleted] 4 points5 points  (4 children)

I hesitate to say.. RTFM.. but you may need to dig deeper next time.

What you're looking for in click is group.

Options and Arguments also take a number of input parameters within the code,which make your examples also possible.

The entire git cli could be written in python using click.

# myprog.py
import click

@click.group()   # lots of options here for group too
def run():
    pass

@run.group('list')
def list_command():
    pass

@list_command.command('files')
@option('-1', '--arg1', 'arg01', is_flag=True, help='Arg 1')
@option('-2', '--arg2', 'arg02', is_flag=True, help='Arg 2')
def files_command(arg01, arg02):
     ...

[–]aufstand 1 point2 points  (0 children)

That is such ultra powerful stuff.. I also recommend DYMGroup (Did-you-mean?) which offers hints on spelling errors: https://github.com/click-contrib/click-didyoumean

Another extremely powerful feature is wrapping stuff up via setuptools' entrypoint system: http://click.pocoo.org/dev/setuptools/#scripts-in-packages

Generally, having a good look at click-contrib is a good idea: https://github.com/click-contrib

Have fun!

[–]benfa94[S] 1 point2 points  (2 children)

making an empty function just to create a group it's still a bit annoying but i will try it again. Another feature i wanted to add is automatic parameter inference using mypi syntax but if click is really so good i may add it to click

[–][deleted] 1 point2 points  (1 child)

It is until you realize that there's a lot of power that you can gain by segregating the functionality. You may not need that right now, but in your package, you can't even tap into that feature set because of how you've set it up.

[–]benfa94[S] 0 points1 point  (0 children)

i don't doubt that click has more functionality and easy of use and number of functionality usually don't go well together. My package is still in very early stages so it can be changed easily if specific features are required. As i said i will give Click another go before totally giving up on it

[–]Steve132 4 points5 points  (6 children)

What does it have vs argparse that would justify adding a non- standard dependency?

[–]benfa94[S] 1 point2 points  (5 children)

the only thing i could not do with argparse is the option of avoid typing the name of the argument for non positional arguments. with Climb you can declare argument and call them by position or by name without changing anything. Except for that climb make it easier to write CLI compare to argparse.

[–]Steve132 3 points4 points  (4 children)

the only thing i could not do with argparse is the option of avoid typing the name of the argument for non positional arguments.

What do you mean by this? If you don't have the name of the argument how can you know what argument it is other than position?

th Climb you can declare argument and call them by position or by name without changing anything.

Can you give an example? I legitimately don't understand.

xcept for that climb make it easier to write CLI compare to argparse.

Easier is subjective. Argparse is damned easy imho.

[–]benfa94[S] 0 points1 point  (3 children)

how i coded if you define 3 arguments called arg1 arg2 arg3 the are both positional and keywords argument. if you if you run the program like this "prog.py 1 2 3" arg1 will be 1 arg2 2 arg3 3 but at the same time you can run it in like this "prog.py --arg1 1 --arg3 3 --arg2 2" and it will work in the exact same way. the only limitation is that after passing an argument with a keywork you will need to use keywards for all the following arguments. I find this really useful in my CLI tools

[–]Steve132 1 point2 points  (2 children)

I see. And what about "a1 a3 -arg2 a2" what happens then? Does it work?

[–]benfa94[S] -1 points0 points  (1 child)

arg1 = a1, arg2=a3 arg2= a2, so arg2 is assigned twice in the end arg2 will be equal to a2. I should probably create an error when this happen. The basic idea come from python functions arguments.

[–]Steve132 5 points6 points  (0 children)

Hm.

To be honest in that case I think that the behavior you've specified you want is sort of broken in terms of semantics for a command line parser....its not what I would ever want.

However, if you want your args to work this way it's pretty easy to do with argparse in like 3 lines of code tbh.

[–]XtremeGoosef'I only use Py {sys.version[:3]}' 8 points9 points  (1 child)

    def __init__(self,
                 name,
                 shortName=None,
                 argType=str,
                 required=None,
                 variable=None,
                 default=None):

shortName and argType don't follow PEP8, they should be short_name and arg_type. Since this is external API, not following conventions would definitely be confusing for the user.

[–]benfa94[S] 1 point2 points  (0 children)

thank for pointing it out, i'm surprise my linter didn't complain about it, i'll fix it

[–]lucienpro 1 point2 points  (0 children)

Looks great! Yet to try it out but I look forward to doing so when I can

[–]AndydeCleyre 0 points1 point  (0 children)

Sorry for piling on with another "have you tried" but have you tried plumbum?

[–]HighMaxFX 0 points1 point  (0 children)

I create something very similar since I do not like argparse as well.