timewarrior
timewarrior copied to clipboard
Allow extensions to take extra options
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?
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>
...
]
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
.
Do you have a concrete use case where argument forwarding is required? It would be helpful to continue this discussion with a proper example... 😉
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 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 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:
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.