· cli python

Python: Click - Handling Date Parameter

I’ve been building a little CLI application using the Python Click Library, and I wanted to pass in a Date as a parameter. There’s more than one way to do this.

Let’s first install the Click library:

pip install click

And now we’ll import our required libraries:

from datetime import date
import click

Now we’ll create a sub command that takes two parameters: date-start and date-end. These parameters have the type DateTime, and we can pass a string in the format yyyy-mm-dd from the command line:

@click.group()
def cli():
    pass


@click.command()
@click.option('--date-start', type=click.DateTime(formats=["%Y-%m-%d"]),
              default=str(date.today()))
@click.option('--date-end', type=click.DateTime(formats=["%Y-%m-%d"]),
              default=str(date.today()))
def dummy(date_start, date_end):
    click.echo(f"Start: {date_start}, End: {date_end} ")


cli.add_command(dummy)

if __name__ == '__main__':
    cli()

We can execute this command by running the following:

$ python blog.py dummy --date-start 2018-01-01
Start: 2018-01-01 00:00:00, End: 2019-07-29 00:00:00

We only passed in date-start, which means date-end has defaulted to today’s date. The dates are correct, but we still have time information that we don’t want. We can get rid of that by calling the date function on those DateTime objects:

@click.command()
@click.option('--date-start', type=click.DateTime(formats=["%Y-%m-%d"]),
              default=str(date.today()))
@click.option('--date-end', type=click.DateTime(formats=["%Y-%m-%d"]),
              default=str(date.today()))
def dummy(date_start, date_end):
    date_start = date_start.date()
    date_end = date_end.date()
    click.echo(f"Start: {date_start}, End: {date_end} ")

Now let’s run the script again:

$ python blog.py dummy --date-start 2018-01-01
Start: 2018-01-01, End: 2019-07-29

Alternatively we can create our own Date type based on the DateTime type:

from datetime import datetime

class Date(click.ParamType):
    name = 'date'

    def __init__(self, formats=None):
        self.formats = formats or [
            '%Y-%m-%d',
            '%Y-%m-%dT%H:%M:%S',
            '%Y-%m-%d %H:%M:%S'
        ]

    def get_metavar(self, param):
        return '[{}]'.format('|'.join(self.formats))

    def _try_to_convert_date(self, value, format):
        try:
            return datetime.strptime(value, format).date()
        except ValueError:
            return None

    def convert(self, value, param, ctx):
        for format in self.formats:
            date = self._try_to_convert_date(value, format)
            if date:
                return date

        self.fail(
            'invalid date format: {}. (choose from {})'.format(
                value, ', '.join(self.formats)))

    def __repr__(self):
        return 'Date'

And then we’ll add a new command that uses this parameter type:

@click.command()
@click.option('--date-start', type=Date(formats=["%Y-%m-%d"]), default=str(date.today()))
@click.option('--date-end', type=Date(formats=["%Y-%m-%d"]), default=str(date.today()))
def dummy2(date_start, date_end):
    click.echo(f"Start: {date_start}, End: {date_end} ")

cli.add_command(dummy2)

And now let’s call this sub command:

$ python blog.py dummy2 --date-start 2018-01-02 --date-end 2019-04-05
Start: 2018-01-02, End: 2019-04-05
  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket