{Never write scripts again}

Smettiamola di scrivere script!

PyCon 23

{ Peter Bittner }

Developer
of people, companies and code

@peterbittner, django@bittner.it

Dizionario Italiano a cura di Enrico Olivetti

banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}
banàle, \text{\small{ba|nà|le}}
\text{privo di carattere, di originalità, molto comune,}
\text{convenzionale, ovvio, poco interessante}

CLI Applications

  • What's wrong with scripts?
  • Why CLI Applications?

 

  1. Argparse
  2. Click
  3. Docopt
# CLI APPLICATIONS
print("This is important code")

for index, arg in enumerate(sys.argv):
    print(f"[{index}]: {arg}")
# CLI APPLICATIONS

What's Wrong with Scripts?

  • Easy to get started
  • Limited possibilities for structure
  • Hard to (unit) test
  • No dependency management
  • Deployment may require care
  • Custom user experience
def main():
    print("Usage: foo <bar> --baz")
    
if __name__ == "__main__":
    main()
# CLI APPLICATIONS

Why CLI Applications?

  • Standardized user experience
  • More possibilities for structure
  • Possibilities for all kinds of testing
  • Dependency management
  • Packaging & distribution
import argparse
from . import __version__

def parse_arguments():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--version', action='version',
                        version=__version__)
    parser.add_argument('filename')
    args = parser.parse_args()
    return args

def main():
    args = parse_arguments()
    ...
# CLI APPLICATIONS

Argparse

import click

@click.command()
@click.version_option()
@click.argument('filename')
def main(filename):
    click.echo(filename)
# CLI APPLICATIONS

Click

import click

@click.command()
@click.version_option()
@click.argument('filename', type=click.Path(exists=True))
def main(filename):
    click.echo(filename)
import click

@click.command()
@click.version_option()
@click.argument('infile', type=click.File())
def main(infile):
    click.echo(infile.read())
"""Foobar
Usage:
  foobar (-h | --help | --version)
  foobar [-s | --silent] <file>
  foobar [-v | --verbose] <file>
Positional arguments:
  file           target file path name
Optional arguments:
  -h, --help     show this help message and exit
  -s, --silent   don't show progress output
  -v, --verbose  explain progress verbosely
  --version      show program's version number and exit
"""
from docopt import docopt
from . import __version__

def parse_arguments():
    args = docopt(__doc__, version=__version__)

    return dict(
        file=args['<file>'],
        silent=args['-s'] or args['--silent'],
        verbose=args['-v'] or args['--verbose'],
    )
# CLI APPLICATIONS

Docopt

$ copier gh:painless-software/python-cli-test-helpers pycon-italia-cli

🎤 engine
   (Use arrow keys)
 » Argparse
   Click
   Docopt
...
# WRITING TESTS

Demo

Thank you!

for your precious time

Painless Software

Less pain, more fun.

Most images taken from Wikipedia (CC-SA)

Alberto Sordi animated GIF stolen from giffetteria.it

Made with Slides.com