10

I would like to use a variable number of arguments in a task for pyinvoke. Like so:

from invoke import task

@task(help={'out_file:': 'Name of the output file.', 
            'in_files': 'List of the input files.'})
def pdf_combine(out_file, *in_files):
    print( "out = %s" % out_file)
    print( "in = %s" % list(in_files))

The above is only one of many variations I tried out but it seems pyinvoke can't handle a variable number of arguments. Is this true?

The above code results in

$ invoke pdf_combine -o binder.pdf -i test.pdf test1.pdf
No idea what '-i' is!

Similar, if I define pdf_combine(out_file, in_file), without the asterisk before in_file

$ invoke pdf_combine -o binder.pdf -i test.pdf test1.pdf
No idea what 'test1.pdf' is!

If I invoke the task with only one in_file like below it run OK.

$ invoke pdf_combine -o binder.pdf -i test.pdf
out = binder.pdf
in = ['t', 'e', 's', 't', '.', 'p', 'd', 'f']

What I would like to see is

$ invoke pdf_combine -o binder.pdf test.pdf test1.pdf test2.pdf
out = binder.pdf
in = [test.pdf test1.pdf test2.pdf]

I could not find anything like that in the documentation of pyinvoke, though I cannot imagine that other users of this library do not have the need for calling a task with a variable number of arguments...

Killwas
  • 103
  • 7
  • Are you getting an error? Please include traceback in your question if so. – Forge Mar 03 '16 at 12:42
  • Thanks, no traceback, the question is more about using the pyinvoke library. I amended my question with a few examples for clarification. – Killwas Mar 03 '16 at 15:43
  • I'm not sure what version of invoke you're using - but you need to use `invoke pdf-combine` (notice it's a dash and not an underscore - as is done in the arguments if they have one http://docs.pyinvoke.org/en/stable/concepts/invoking-tasks.html#dashes-vs-underscores-in-flag-names ) – Marc Apr 08 '20 at 18:29

2 Answers2

4

You can do something like this:

from invoke import task

@task
def pdf_combine(out_file, in_files):
    print( "out = %s" % out_file)
    print( "in = %s" % in_files)
    in_file_list = in_files.split(',')   # insert as many args as you want separated by comma

>> out = binder.pdf
>> in = test.pdf,test1.pdf,test2.pdf

Where the invoke command is:

invoke pdf_combine -o binder.pdf -i test.pdf,test1.pdf,test2.pdf

I couldn't find another way to do this reading the pyinvoke documentation.

Forge
  • 6,538
  • 6
  • 44
  • 64
  • 1
    Thanks. This looks like a practical and clean solution. I'm happy I did not miss anything in the documentation. Meanwhile I've used the argparse module instead. It needs 20 minutes more to understand, but it was worth doing it. I just got a glimpse of its power. Of course, pyinvoke does not aim into the same direction as argparse. Fair enough. – Killwas Mar 04 '16 at 13:43
1

As of version 0.21.0 you can use iterable flag values:

@task(
  help={
      'out-file': 'Name of the output file.', # `out-file` NOT `out_file`
      'in-files': 'List of the input files.'},
  iterable=['in_files'],
)
def pdf_combine(out_file, in_files):
    for item in in_files:
        print(f"file: {item}")

NOTE help using the dash converted key and iterable using the non-converted underscore key

NOTE I understand the above note is kinda odd, so I submitted a PR since the author is pretty awesome and might take the suggestion into consideration

Using it in this way allows for this type of cli:

$ invoke pdf-combine -o spam -i eggs -i ham
  file: eggs
  file: ham

$ invoke --help pdf-combine
Usage: inv[oke] [--core-opts] pdf-combine [--options] [other tasks here ...]

Docstring:
  none

Options:
  -i, --in-files                 List of the input files
  -o STRING, --out-file=STRING   Name of the output file.

NOTE pdf_combine task is called with inv pdf-combine from the CLI

Marc
  • 4,820
  • 3
  • 38
  • 36