Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Click - PyCaribbean 2017 - Puerto Rico

Seb
February 18, 2017

Click - PyCaribbean 2017 - Puerto Rico

Seb

February 18, 2017
Tweet

More Decks by Seb

Other Decks in Technology

Transcript

  1. Click
    A Pleasure To Write
    A Pleasure To Use
    Sebastian Vetter
    @elbaschid
    Slides: http://bit.ly/click-slides

    View Slide

  2. Seb
    • @elbaschid
    • Living in Vancouver !
    • Python Engineer at Eventbase

    View Slide

  3. Why This Talk?

    View Slide

  4. Terminology
    https://www.flickr.com/photos/chris_schultz/11306328493

    View Slide

  5. Parameter
    • Argument
    • Option

    View Slide

  6. Argument
    • Mandatory parameter
    • Only values required
    What It Looks Like:
    $ pgcli postgresql://....

    View Slide

  7. Option
    • Optional parameter
    • Name and value required
    What It Looks Like:
    $ heroku --help
    $ heroku logs --app my-heroku-app

    View Slide

  8. (Sub-)Command
    • Nested commands allowed
    • Groups sub-commands
    • Has options & arguments
    What It Looks Like:
    $ pip install django

    View Slide

  9. In Plain Python
    https://www.flickr.com/photos/tutam/5008073062/

    View Slide

  10. Most Basic CLI
    import sys
    if len(sys.argv) <= 1:
    print('You need to give me an argument')
    sys.exit(1)
    args = sys.argv[1:]
    print('Your argument are: {}'.format(args))

    View Slide

  11. The Limitations
    • Manual parsing
    • No input validation
    • No help text formatting

    View Slide

  12. Choose A Library
    https://www.flickr.com/photos/[email protected]/3778738056

    View Slide

  13. Standard Library
    • optparse
    • argparse

    View Slide

  14. There Are Others
    • docopt
    • clint
    • ...many more...

    View Slide

  15. Introducing Click
    https://www.flickr.com/photos/[email protected]/7155464950

    View Slide

  16. click
    • Author: Armin Ronacher
    • Version 6.x
    • http://click.pocoo.org/6/

    View Slide

  17. click
    • Intuitive
    • Nestable and composable
    • Better handling of input and output
    • Why Click?

    View Slide

  18. Let's Build A CLI
    https://www.flickr.com/photos/[email protected]/3967634792

    View Slide

  19. pip install click

    View Slide

  20. Setting Up A CLI
    https://www.flickr.com/photos/sveinhal/2416609728

    View Slide

  21. On Github
    • Click Template: https://github.com/elbaschid/cc-python-cli
    • Example Code: bit.ly/pycon-click-example

    View Slide

  22. Create A Project
    $ pip install cookiecutter
    $ cookiecutter https://github.com/elbaschid/cc-python-cli
    ...
    $ cd my-!-tool
    $ pip install -e .
    $ my_!_tool
    I am the my_!_tool CLI

    View Slide

  23. Decorator-based approach
    # my_!_tool/cli.py
    import click
    @click.command()
    def main():
    click.secho('I am the my_!_tool CLI', fg='blue')

    View Slide

  24. Our Example
    https://www.flickr.com/photos/judy-van-der-velden/14687818499

    View Slide

  25. Ad Notifications
    • Parsing a given search URL
    • Extracting ads
    • Ignore "seen" ads
    • Email notifications

    View Slide

  26. Specify a URL

    View Slide

  27. Run it!
    $ ad_notifier "http://www.pinkbike.com/buysell/..."
    Processing URL: http://www.pinkbike.com/buysell/list/...
    Found 20 ads!

    View Slide

  28. Arguments
    https://www.flickr.com/photos/alper/2781645509

    View Slide

  29. Add an argument
    # ad_notifier/cli.py
    @click.command()
    @click.argument('url')
    def main(url):
    click.echo('Processing URL:', url)
    ads = find_ads(url)
    click.echo('Found {} ads!'.format(len(ads)))

    View Slide

  30. Send an Notification Email

    View Slide

  31. Run it!
    $ ad_notifier "http://..." --email [email protected]
    Processing URL: http://...
    Found 20 ads!
    Sending email to [email protected]

    View Slide

  32. Options
    https://www.flickr.com/photos/photographingtravis/15427838493

    View Slide

  33. Add Email Option
    @click.command()
    @click.argument('url')
    @click.option('--email', envvar='EMAIL_ADDRESS')
    def main(url, email):
    ...
    if email and ads:
    send_email(email, ads)

    View Slide

  34. Handle New Ads Only

    View Slide

  35. Run it!
    $ ad_notifier "http://..."
    ...
    Found 0 ads!
    $ ad_notifier "http://..." --reset-cache
    ...
    Found 20 ads!

    View Slide

  36. Flags
    https://www.flickr.com/photos/pennstatelive/16216912657

    View Slide

  37. Reset the cache
    @click.command()
    ...
    @click.option('--reset-cache', default=False, is_flag=True)
    def main(url, email, reset_cache):
    click.echo('Processing URL:', url)
    new_ads = find_new_ads(url, reset_cache)
    click.echo('Found {} ads!'.format(len(new_ads)))
    ...

    View Slide

  38. Make it a Periodic Task

    View Slide

  39. Run it!
    $ # With scheduler
    $ ad_notifier run_periodically \
    --run-every 5 --email [email protected]
    $ # Once
    $ ad_notifier run_once \
    --email [email protected]

    View Slide

  40. (Sub-)Commands
    (https://www.flickr.com/photos/soldiersmediacenter/5754070333)

    View Slide

  41. Add a run & schedule command
    @click.group()
    def main():
    pass
    @main.command()
    ...
    def run_once(url, email, reset_cache):
    pass
    @main.command()
    ...
    @click.option('--run-every', default=5, type=int)
    def run_periodically(..., run_every):
    pass

    View Slide

  42. Not Very DRY
    @main.command()
    @click.argument('url')
    @click.option('--email')
    @click.option('--reset-cache', default=False, is_flag=True)
    def run_once(url, email, reset_cache):
    ...
    @main.command()
    @click.argument('url')
    @click.option('--email')
    @click.option('--reset-cache', default=False, is_flag=True)
    @click.option('--run-every', default=5, type=int)
    def run_periodically(url, email, reset_cache, run_every):
    ...

    View Slide

  43. Context
    https://www.flickr.com/photos/chanceprojects/16286089602

    View Slide

  44. Using the Context obj
    @click.group()
    @click.argument('url')
    @click.option('--email')
    @click.option('--reset-cache', ...)
    @click.pass_context
    def main(context, url, email, reset_cache):
    context.obj = {
    'url': url,
    'email': email,
    'reset_cache': reset_cache}

    View Slide

  45. Updating Sub-Commands
    @main.command()
    @click.pass_context
    def run_once(context):
    process_url(**context.obj)

    View Slide

  46. Add schedule subcommand
    @main.command()
    @click.option('--run-every', default=5, type=int)
    @click.pass_context
    def run_periodically(context, run_every):
    schedule.every(run_every).minutes.do(
    process_url, **context.obj)
    while True:
    schedule.run_pending()
    time.sleep(1)

    View Slide

  47. The Final Command
    $ # With scheduler
    $ ad_notifier run_periodically \
    --run-every 5 --email [email protected]
    $ # Once
    $ ad_notifier run_once \
    --email [email protected]

    View Slide

  48. ! We Did It !

    View Slide

  49. Improve Documentation

    View Slide

  50. No Effort
    $ ad_notifier
    Usage: ad_notifier [OPTIONS] URL COMMAND [ARGS]...
    Options:
    --email TEXT
    --reset-cache
    --help Show this message and exit.
    Commands:
    run_once
    run_periodically

    View Slide

  51. Adding help text
    @click.option('--reset-cache', default=False, is_flag=True,
    help='reset the internal ads cache')
    @click.pass_context
    def main(context, url, email, reset_cache):
    """
    Check pinkbike ads for URL and (optionally) send email notification.
    """

    View Slide

  52. A Little Effort
    $ ad_notifier
    Usage: ad_notifier [OPTIONS] URL COMMAND [ARGS]...
    Check pinkbike ads for URL and (optionally) send email notification.
    Options:
    --email TEXT email to send notifications to
    --reset-cache reset the internal ads cache
    --help Show this message and exit.
    Commands:
    run_once Run check for new ads once.
    run_periodically Run scheduler to check for new ads...

    View Slide

  53. Also Check Out
    • Environment Variables
    • Parameter Types
    • Testing
    • Bash Autocomplete

    View Slide

  54. Questions?
    https://www.flickr.com/photos/thomashawk/9058066607

    View Slide

  55. Click
    A Pleasure To Write
    A Pleasure To Use
    Sebastian Vetter
    @elbaschid
    Slides: http://bit.ly/click-slides

    View Slide