Parameters in Python Click Library

Parameters in Python Click Library

Parameters in Click
Click supports two types of parameters for scripts: options and arguments.As its name
indicates, an option is optional. While arguments can be optional within reason, they
are much more restricted in how optional they can be. Arguments should be used 
exclusively for things like going to subcommands or input filenames / URLs, and have 
everything else be an option instead.

Arguments Vs Options:
Options are more useful than arguments as they have the following features :
--automatic prompting for missing input
--act as flags (boolean or otherwise)
--option values can be pulled from environment variables, arguments can not

Options are fully documented in the help page, arguments are not (this is 
intentional as arguments might be too specific to be automatically documented).On 
the other hand arguments, unlike options, can accept an arbitrary number of 
arguments. Options can strictly ever only accept a fixed number of arguments
(defaults to 1).

Parameter Types
Parameters can be of different types. Types can be implemented with different
behaviour and some are supported out of the box:

click.STRING:The default parameter type which indicates unicode strings.
click.INT:A parameter that only accepts integers.
click.FLOAT:A parameter that only accepts floating point values.
click.BOOL:A parameter that accepts boolean values. This is automatically used for
boolean flags. If used with string values 1, yes, y, t and true convert to True 
and 0, no, n, f and false convert to False.

click.UUID:a parameter that accepts UUID values. This is not automatically 
guessed but represented as uuid.UUID.

Example: import click @click.command() @click.argument('name', type=click.STRING, required=True) @click.argument('num', type=click.INT, required=True) @click.argument('floating_number', type=click.FLOAT, required=False) @click.argument('true_or_false', type=click.BOOL , required=True) @click.argument('uuid', type=click.UUID , required=True) def cli(name,num,floating_number,true_or_false,uuid): click.echo('Hello %s!'%name) click.echo('Your favourite integer is %d.' %num) click.echo('Your favourite floating number is %8.2f.' %floating_number) click.echo('Your said True or False: %s.' %true_or_false) click.echo('Your said True or False: %d.' %true_or_false) click.echo('The input uuidentifier is %s.' %uuid) To run the above script you pass a command like: $ myhello Ranjit 7 1.1 True {00010203-0405-0607-0809-0a0b0c0d0e0f} Hello Ranjit! Your favourite integer is 7. Your favourite floating number is 1.10. Your said True or False: True. Your said True or False: 1. The input uuidentifier is 00010203-0405-0607-0809-0a0b0c0d0e0f.
class click.File(mode='r', encoding=None, errors='strict', lazy=None, atomic=False)
Declares a parameter to be a file for reading or writing. The file is automatically 
closed once the context tears down (after the command finished working).Files can be
opened for reading or writing. The special value - indicates stdin or stdout 
depending on the mode.By default, the file is opened for reading text data, but it
can also be opened in binary mode or for writing. The encoding parameter can be used
to force a specific encoding.The lazy flag controls if the file should be opened
immediately or upon first IO. The default is to be non-lazy for standard input and 
output streams as well as files opened for reading, lazy otherwise. When opening a
file lazily for reading, it is still opened temporarily for validation, but will not
be held open until first IO. lazy is mainly useful when opening for writing to avoid 
creating the file until it is needed.

For example we can modify our script to write the arguments passed to an output.txt
file as below:

import click

@click.argument('name', type=click.STRING, required=True)
@click.argument('num', type=click.INT, required=True)
@click.argument('floating_number', type=click.FLOAT, required=False)
@click.argument('true_or_false', type=click.BOOL , required=True)
@click.argument('uuid', type=click.UUID , required=True)
@click.argument('output', type=click.File('wb'))

def cli(name,num,floating_number,true_or_false,uuid,output):
       output.write(bytes("Hello " + name,'utf-8'))
       output.write(bytes("\nYou entered integer:"+ str(num),'utf-8'))
       output.write(bytes(str('\nThe float is  {0} '.format(floating_number)),'utf-8'))
       output.write(bytes("\nYou entered boolean:"+ str(true_or_false),'utf-8'))
       output.write(bytes("\nYou entered UUID:"+ str(uuid),'utf-8'))
Command to run the script above is :
$myhello Ranjit 7 1.1 True {00010203-0405-0607-0809-0a0b0c0d0e0f} output.txt

The file output.txt is created and its contents are as follows.
Hello Ranjit
You entered integer:7
The float is  1.1 
You entered boolean:True
You entered UUID:00010203-0405-0607-0809-0a0b0c0d0e0f

class click.Path(exists=False, file_okay=True, dir_okay=True, writable=False, 
readable=True, resolve_path=False, allow_dash=False, path_type=None).The path type
is similar to the File type but it performs different checks. First of all, instead
of returning an open file handle it returns just the filename. Secondly, it can 
perform various basic checks about what the file or directory should be.

class click.Choice(choices, case_sensitive=True)
The choice type allows a value to be checked against a fixed set of supported values.
All of these values have to be strings.You should only pass a list or tuple of 
choices. Other iterables (like generators) may lead to surprising results.
The following script  prints a greeting based on choice made from the list of 

import click

@click.option('--message_type', type=click.Choice(['Welcome', 'Hello']))
def cli(message_type):
     click.echo( message_type + ' Ranjit!')

Use --help to find out the choices available and then type the command as below to 
get the output:
$myhello --message_type='Hello'
Hello Ranjit!

class click.IntRange(min=None, max=None, clamp=False)
IntRange parameter type works very similarly to the INT type, but restricts the 
value to fall into a specific range (inclusive on both edges). It has two modes:
--the default mode (non-clamping mode) where a value that falls outside of the range
  will cause an error.
--an optional clamping mode where a value that falls outside of the range will be
  clamped. This means that a range of 0-5 would return 5 for the value 10 or 0 for 
  the value -1 (for example).

An example:
import click

@click.option('--count', type=click.IntRange(0, 20, clamp=True))
@click.option('--digit', type=click.IntRange(0, 10))
def cli(count, digit):
    click.echo(str(digit) * count)

To run the script above:
$myhello --count=10 --digit=5

$myhello --count=110 --digit=11
Usage: myhello [OPTIONS]
Try "myhello --help" for help.

Error: Invalid value for "--digit": 11 is not in the valid range of 0 to 10.

class click.FloatRange(min=None, max=None, clamp=False)
A parameter that works similar to click.FLOAT but restricts the value to fit into 
a range. The default behaviour is to fail if the value falls outside the range, but 
it can also be silently clamped between the two edges.

class click.DateTime(formats=None)
The DateTime type converts date strings into datetime objects.The format strings 
which are checked are configurable, but default to some common (non-timezone aware) 
ISO 8601 formats.When specifying DateTime formats, you should only pass a 
list or a tuple. Other iterables, like generators, may lead to surprising results.
The format strings are processed using datetime.strptime, and this consequently 
defines the format strings which are allowed.

formats %u2013 A list or tuple of date format strings, in the order in which they should 
be tried. Defaults to '%Y-%m-%d', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S'.

Parameter Names:

Parameters (both options and arguments) accept a number of positional arguments which
are passed to the command function as parameters. Each string with a single dash is
added as a short argument; each string starting with a double dash as a long one.
If a string is added without any dashes, it becomes the internal parameter name which
is also used as variable name.If all names for a parameter contain dashes, the 
internal name is generated automatically by taking the longest argument and 
converting all dashes to underscores.

The internal name is converted to lowercase.
For an option with ('-n', '--name'), the parameter name is name

For an option with ('-x',), the parameter is x.

For an option with ('-f', '--filename', 'dest'), the parameter name is dest.

For an option with ('--CamelCaseOption',), the parameter is camelcaseoption.

For an arguments with ('dangle'), the parameter name is dangle.

Custom Type Parameters

To implement a custom type, you need to subclass the ParamType class. Types can be
invoked with or without context and parameter object, which is why they need to be 
able to deal with this.
The following code implements an integer type that accepts hex and octal numbers in 
addition to normal integers, and converts them into regular integers:

class BasedIntParamType(click.ParamType):
    name = 'integer'

    def convert(self, value, param, ctx):
            if value[:2].lower() == '0x':
                return int(value[2:], 16)
            elif value[:1] == '0':
                return int(value, 8)
            return int(value, 10)
        except ValueError:
  '%s is not a valid integer' % value, param, ctx)
@click.option('--value', '-f', type=BasedIntParamType(),prompt="What do you want ?")
def cli(value):

As we see above, the subclass needs to implement ParamType.comvert() method.
To execute the above code, we pass the following commands and get the respective 
integer values:
$myhello --value='0x12a'
$myhello --value='0777'