Programming

  • Quick guide to convert a Python script into a CLI tool

    Reading the Click’s documentation will bring you up to speed on what it can do, but I’m writing a quick list of steps I follow every time I need to make a quick CLI out of a Python script.

    So, let’s start by creating a Python project and installing Click as a dependency.

    $ poetry new cool-tool
    $ cd cool-tool
    $ poetry add click

    Poetry will create a scaffold directory that looks like this:

    cool-tool/
      cool_tool/
        __init__.py
      poetry.lock
      pyproject.toml

    For this example, we will create a main.py file with a very cool function.

    $ cd cool-tool 
    $ touch cool_tool/main.py

    In our file we create a function and some logic.

    # main.py
    def cool():
      print("hello cool world")

    OK, now to the part we really care. Let’s convert this script into a CLI

    • create a directory named “console” inside our main module.
    • create a __init__.py file to let Python know this is a module.
    • create a run.py file where we will add our Click logic.
    $ mkdir console
    $ touch console/__init__.py
    $ touch console/run.py

    Inside our run.py we add:

    import os
    import click
    from cool_tool.main import cool
    
    @click.group
    def cli():
       pass
    
    
    @click.command
    @click.argument("cool")
    def show_cool():
       click.echo(cool())

    Then we add our CLI script to our pyproject.toml so we can use it directly inside our virtual environment, e.g., cool --help. Add this section and we should be done:

    # pyproject.toml
    [tool.poetry.scripts]
    cool = 'cool_tool.console.run:cli'

    Run poetry install, make sure you have the virtual environment activated, and verify everything works by running the help command.

    $ poetry install
    $ poetry shell
    $ cool --help
    
    Usage: cool [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help Show this message and exit.
    
    Commands:
      show_cool

    Resources

  • Python function to send a single Telegram bot message

    This is my console.log equivalent when I need a quick observability implementation.

    Usually, when you build a Telegram bot, you have to have a small server listening for updates.

    The server logic is abstracted away as a simple API using the python-telegram-bot package, but we can write a simpler implementation for a single message:

    import os
    import telegram
    
    TELEGRAM_BOT_ID = os.getenv("TELEGRAM_BOT_ID")
    TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
    
    async def send_telegra_notification(msg: str, origin: str = "AppName:main"):
        notification_msg = f"*{origin}*: {msg}"
        bot = telegram.Bot(token=TELEGRAM_BOT_TOKEN)
        try:
            await bot.initialize()
            await bot.send_message(
                chat_id=TELEGRAM_BOT_ID,
                text=notification_msg,
                parse_mode=telegram.constants.ParseMode.MARKDOWN,
            )
        except Exception as e:
            # handle exception, e.g., logger.error(e)
            print(e)
        finally:
            await bot.shutdown()
    
  • Deploy PostgREST to Dokku

    Here is a list of steps to deploy a PostgREST instance to Dokku.

    Create a new app.

    dokku apps:create pgrest-myproject

    Add a port mapping from 3000 (PostgREST container) to 80 (host).

    dokku ports:add pgrest-myproject http:80:3000

    Create a new PostgreSQL database using the dokku-postgres plugin and link it to our project.

    dokku postgres:create pgrest-myproject
    dokku posgres:link pgrest-myproject pgrest-myproject

    A new environment variable will be created:

    =====> pgrest-myproject
    DATABASE_URL: postgres://postgres:alongpass@dokku-postgres-pgrest-myproject:5432/pgrest_myproject

    But we need to define the same value in a variable named PGRST_DB_URI so PostgREST can access the database.

    dokku config:set pgrest-myproject PGRST_DB_URI="postgres://postgres:alongpass@dokku-postgres-pgrest-myproject:5432/pgrest_myproject"

    Pull and deploy the Docker image from dockerhub.

    docker pull postgrest/postgrest:latest
    docker tag postgrest/postgrest:latest dokku/postgrest:latest
    dokku git:from-image pgrest-myproject dokku/postgrest:latest

    Add a Let’s Encrypt TLS certificate with the Dokku letsencrypt plugin.

    dokku letsencrypt:set pgrest-myproject email <your-email>
    dokku letsencrypt:enable pgrest-myproject
  • Launch a simple HTTP Server in One Line of Python

    I always google this snippet when I need it, so I will put it here for easier access.

    python3 -m http.server 8080 -d ./public
  • Git commit messages

    Each commit message consists of a header, a body, and a footer.

    <type>(<scope>): <subject>
    <BLANK LINE>
    <body>
    <BLANK LINE>
    <footer>

    Header

    The header is mandatory and the scope is optional.

    Type

    Must be one of the following:

    • feat: introduces a new feature to the codebase
    • fix: patches a bug in your codebase
    • refactor: improves the code without changing functionality
    • BREAKING CHANGE: major version

    Additional types:

    • build: a change that affects the build system or external dependencies
    • chore: same as build but chore is preferred, also any change that doesn’t fit in any other type
    • ci: changes to the CI configuration
    • docs: changes to the documentation
    • style: white-space, formatting, semi-colons, etc.
    • perf: performance improvements
    • test: adding or fixing

    Scope

    The scope can be empty. Scope can be any of these:

    • category, part or section of the codebase we are affecting, e.g.: api, client, tooling
    • a specific package, library or dependency,e.g.: moment.js, requests
    • a ticket number in case the current commit doesn’t close it, e.g.: chore(123): update dependencies

    Subject

    Short (50 chars or less) summary of the change:

    • use the imperative, present tense: “change” not “changed”
    • don’t capitalize the first letter
    • no dot (.) at the end

    Footer

    The footer should contain a closing reference to an issue if any and breaking changes.

    Closes #123, #345
    
    BREAKING CHANGE:
    
    IE6 not supported anymore.

    Semantic versioning

    Conventional commits are designed to work with Semantic versioning.

    • feat: minor version
    • fix: patch version
    • BREAKING CHANGE: major version

    Examples

    Add a new feature without breaking existing functionality. This bumps a minor version, e.g.: 1.2.0 to 1.3.0.

    feat: add search for transactions
    
    Add new React component for searching transactions with autocompletion.
    
    Include tests.
    
    Closes #123, #234

    Fix a bug. This bumps a patch version, e.g.: 1.3.0 to 1.3.1.

    bug(api): add correct status code for existing resources
    
    We return `409` when trying to create a user with an existing email.
    
    Closes #453

    Add new feature that breaks existing functionality. This bumps a major version, e.g.: 1.3.1 to 2.0.0.

    feat!: add bank sync 
    
    This replace CSV import and use Plaid to connect to people's banks.
    
    Closes #423, #424, #425
    
    BREAKING CHANGE:
    
    People won't be able to import transactions using CSV. Only Bank 
    Sync is available.

    Resources