timewarrior icon indicating copy to clipboard operation
timewarrior copied to clipboard

Allow extensions to take extra options

Open quazgar opened this issue 5 years ago • 7 comments

It would be great if extensions could take extra arguments from the command line. Maybe everything after a -- could be passed directly to the extension, unparsed by timew?

quazgar avatar Jun 11 '19 11:06 quazgar

Timewarrior transfers all its configuration to the extension. So you could use this approach:

timew myreport rc.<key1>=<val1> rc.<key2>=<val2>

which will output

...
key1: val1
key2: val2
...

[
<Interval data>
...
]

lauft avatar Aug 01 '19 05:08 lauft

I also use arguments in an extension. I've been using environment variables, since I didn't know about the rc method. That seems preferable though, so good to have a workaround for now - thanks! But I do think proper argument forwarding would be much nicer. Then it would be possible to define an argument parser in the extension, provide help, etc, with for example argparse or click.

matt-snider avatar Aug 31 '20 20:08 matt-snider

Do you have a concrete use case where argument forwarding is required? It would be helpful to continue this discussion with a proper example... 😉

lauft avatar Sep 01 '20 10:09 lauft

Hey @lauft, thanks for the quick response :smile:.

My use case looks something like this (adapted to use the rc approach):

$ timew invoice <range> rc.invoice_id=16 rc.invoice_date=2020-08-31 rc.hourly_rate=10

The extension is a Python script that generates a PDF invoice. I'm using your timew-report library btw.

matt-snider avatar Sep 01 '20 12:09 matt-snider

@matt-snider Thanks for the example! I see you have extra data you want to incorporate into your invoice that cannot be hardcoded in the Timewarrior configuration. Passing arguments to the extension sound like a good solution for that.

However I see a problem to determine whether an argument is meant for Timewarrior or the extension. What if Timewarrior has a command/hint with the same name? Who should execute it? One could solve this by passing all arguments to the extension as well (and accept possible clashes) or by passing only those Timewarrior did not consume (which is currently equal to 'none'). 🤔

A quick solution for you may be a wrapper script (e.g. my-invoice) which could look like this:

#!/bin/bash

ARGS=()

until [[ -z "${1-}" ]] ; do
  case "${1}" in
    --id)
      shift
      ID=${1}
      ;;
    --date)
      shift
      DATE=${1}
      ;;
    --rate)
      shift
      RATE=${1}
      ;;
    *)
      ARGS=( "${ARGS[@]" "${1}" )
      ;;
    esac
    shift
done

timew invoice ${ARGS[@]} rc.invoice_id=${ID?"Id missing!"} rc.invoice_date=${DATE?"Date missing!"} rc.hourly_rate=${RATE-10}

so you have command line calls like

my-invoice --id <ID> --date <DATE> [--rate=<RATE>] <TIMEW-ARGS>

Here - as you can see - you also have the problem of separating Timewarrior and report arguments. I solved this by using options for the report arguments. This has the advantage that some input may be optional, e.g. the hourly rate in the wrapper above -- but you could use positional arguments as well.

I'm using your timew-report library btw.

❤️

lauft avatar Sep 02 '20 11:09 lauft

@lauft Thanks for the detailed response :smile:

It's a good question you pose, regarding which arguments the extension should receive. I think the simplest solution would be to pass all arguments to the extension. I don't think the extension seeing arguments already handled by timewarrior is a bad thing, and it could potentially even be useful.

Of course, if timewarrior introduces a new global option and it collides with the extension down the line, that isn't great. You could instruct extension authors to prefix their parameters to avoid that - e.g. --ext-id (invoice id), --ext-rate (invoice rate), --ext-date (invoice date), etc (or even --x-id, --x-rate, etc). I guess that would be safer, but it's also less convenient.

The wrapper script is a nice idea! I think that's the best option for now - thank you :+1:

matt-snider avatar Sep 06 '20 08:09 matt-snider

I see a problem to determine whether an argument is meant for Timewarrior or the extension

Timewarrior could pass anything after -- to the extension, as original poster suggested. Of course then one has to "know" to do this, but it seems reasonable.

It could also work like, the first unrecognized argument triggers end of processing, so that one and all the rest would be passed to the script without looking at them.

smemsh avatar Feb 04 '22 06:02 smemsh