Invoke - Task Management

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction

Automating routine tasks — a core component of modern software development. Whether you need to build a project, deploy applications, format code, run database migrations, or execute tests — dedicating these processes to specialized tools boosts efficiency and reliability. For Python developers, the Invoke library offers a universal, Python‑native solution for task automation.

Invoke is a powerful Python library that lets you create command‑line utilities where tasks are defined as plain Python functions. It provides a clean, functional approach to organizing automated commands, similar to classic tools like make or Fabric, but written entirely in Python.

What Is Invoke?

Invoke — a Python library for defining and running command‑line style tasks, designed to simplify workflow automation for developers. Extracted from Fabric 2, it now serves as an independent component for building CLI interfaces and automating repetitive tasks.

Key Benefits of Invoke

The library equips developers with the following capabilities:

  • Declarative task definitions — write tasks as regular Python functions decorated with @task
  • Automatic CLI generation — the command‑line interface is built automatically from your code
  • Rich argument support — built‑in handling of arguments, options, and help documentation
  • Shell integration — run shell commands effortlessly via the Context object
  • Flexible configuration — support for namespaces, configuration files, and environment variables
  • Extensibility — create complex pipelines and task chains

Installation and Initial Setup

Installing the Library

Install Invoke with pip:

pip install invoke

After installation, the inv command becomes available, acting as a Python‑powered make‑style tool.

Verifying the Installation

Confirm a successful installation:

inv --version

Task Structure and Basic Syntax

Creating Your First Task

A minimal task example looks like this:

from invoke import task

@task
def hello(c):
    print("Hello, world!")

Run the task with:

inv hello

The Context Object

Each task receives a Context object (commonly named c) that lets you run shell commands, manage configuration, and access utility methods:

@task
def build(c):
    c.run("python setup.py sdist")
    c.run("python setup.py bdist_wheel")

The @task Decorator

The @task decorator marks a function as a CLI‑exposed task. It supports numerous parameters for fine‑tuning behavior:

@task(help={"name": "User name to greet"})
def greet(c, name="Anonymous"):
    print(f"Hello, {name}!")

Run with arguments:

inv greet --name=Ivan

Working with Arguments and Options

Required Arguments

Define required arguments by creating function parameters without default values:

@task
def say(c, word):
    print(f"Spoken: {word}")
inv say --word="Hello, world!"

Optional Arguments

Optional arguments are created with default values:

@task
def ping(c, count=1):
    for i in range(count):
        print(f"ping #{i+1}")

Boolean Flags

Create boolean flags by using parameters with a default of False:

@task
def debug(c, verbose=False):
    if verbose:
        print("Verbose mode enabled")
    print("Running debug tasks...")
inv debug --verbose

Argument Type Hinting

Invoke automatically infers argument types, simplifying command‑line usage:

@task
def process_data(c, input_file, output_file="result.txt", batch_size=100, dry_run=False):
    if dry_run:
        print(f"Dry‑run: processing {input_file} → {output_file}")
    else:
        print(f"Processing {batch_size} records from {input_file}")

Task Grouping

Using Collection

Organize related tasks into logical groups with the Collection class:

from invoke import Collection, task

@task
def clean(c):
    c.run("rm -rf build/")

@task
def build(c):
    c.run("python setup.py build")

@task
def install(c):
    c.run("python setup.py install")

# Create a collection
ns = Collection()
ns.add_task(clean)
ns.add_task(build)
ns.add_task(install)

Loading from Modules

Automatically import all tasks from a module:

from invoke import Collection
import my_tasks_module

ns = Collection.from_module(my_tasks_module)

Nested Collections

Invoke supports nested collections for deeper project organization:

# Sub‑collections
build_tasks = Collection('build')
build_tasks.add_task(clean)
build_tasks.add_task(build)

test_tasks = Collection('test')
test_tasks.add_task(unit_tests)
test_tasks.add_task(integration_tests)

# Root collection
ns = Collection()
ns.add_collection(build_tasks)
ns.add_collection(test_tasks)

Project Configuration

Configuration File invoke.yaml

Store configuration in a YAML file at the project root:

run:
  echo: true
  pty: true
  warn: false

project:
  name: "MyProject"
  version: "1.0.0"
  
database:
  host: "localhost"
  port: 5432
  name: "mydb"

Programmatic Configuration

Define configuration directly in Python code:

from invoke import Config, Context

config = Config(overrides={
    "run": {
        "echo": True,
        "pty": True
    },
    "project": {
        "name": "MyProject"
    }
})

ctx = Context(config=config)

Accessing Configuration Inside Tasks

@task
def deploy(c):
    project_name = c.config.project.name
    print(f"Deploying project: {project_name}")

Core Methods of the Context Class

The Context object provides a rich API for interacting with the system and managing the execution environment:

Method Description Example
run(command, **kwargs) Executes a shell command c.run("ls -la")
sudo(command, **kwargs) Runs a command with elevated privileges c.sudo("systemctl restart nginx")
cd(path) Context manager for changing the working directory with c.cd("/var/www"): c.run("git pull")
prefix(command) Prepends a command prefix within a block with c.prefix("source venv/bin/activate"): c.run("python script.py")
config Access to configuration values c.config.database.host

Context Usage Examples

Working with Directories

@task
def deploy(c):
    with c.cd("/var/www/myapp"):
        c.run("git pull origin main")
        c.run("pip install -r requirements.txt")
        c.run("python manage.py migrate")

Using Prefixes

@task
def test_in_venv(c):
    with c.prefix("source venv/bin/activate"):
        c.run("python -m pytest tests/")
        c.run("coverage report")

Error Handling

@task
def safe_deploy(c):
    result = c.run("git pull", warn=True)
    if not result.ok:
        print("Error updating code")
        return False
    
    result = c.run("systemctl restart app", warn=True)
    return result.ok

Controlling Output

@task
def quiet_operation(c):
    # Hide all output
    result = c.run("ls -la", hide=True)
    print(f"Found files: {len(result.stdout.splitlines())}")
    
    # Show only stderr
    c.run("make build", hide="stdout")

Advanced Features

Task Chains

Define task dependencies to create automated pipelines:

@task
def clean(c):
    c.run("rm -rf build/ dist/")

@task
def build(c):
    c.run("python setup.py build")

@task
def test(c):
    c.run("python -m pytest")

@task(pre=[clean, build, test])
def deploy(c):
    c.run("python setup.py sdist upload")

Conditional Execution

@task
def conditional_task(c, environment="development"):
    if environment == "production":
        c.run("python manage.py collectstatic --noinput")
    
    c.run(f"python manage.py migrate --settings=settings.{environment}")

Interactive Tasks

@task
def interactive_deploy(c):
    response = input("Deploy to production? (y/N): ")
    if response.lower() == 'y':
        c.run("fab production deploy")
    else:
        print("Deployment cancelled")

Comprehensive Table of Invoke Methods and Functions

Component Method / Function Description Parameters
@task @task() Decorator for creating CLI tasks name, aliases, help, default, pre, post
Context run() Execute a shell command command, warn, hide, pty, echo, env
Context sudo() Execute a command with sudo privileges command, password, user, warn, hide
Context cd() Change the working directory path
Context prefix() Add a command prefix within a block command
Context config Access configuration values -
Collection add_task() Add a task to a collection task, name, aliases
Collection add_collection() Add a sub‑collection collection, name
Collection from_module() Load tasks from a module module, name, config
Config __init__() Create a configuration object overrides, defaults, lazy
Config load_file() Load configuration from a file path, format
Program run() Execute a program argv, exit
Runner run() Low‑level command execution command, shell, warn, hide, pty

Practical Usage Examples

DevOps and CI/CD

from invoke import task

@task
def lint(c):
    """Run code style checks"""
    c.run("flake8 .")
    c.run("black --check .")
    c.run("mypy .")

@task
def test(c, coverage=False):
    """Execute test suite"""
    cmd = "python -m pytest"
    if coverage:
        cmd += " --cov=src --cov-report=html"
    c.run(cmd)

@task
def build(c):
    """Build distribution packages"""
    c.run("python setup.py sdist bdist_wheel")

@task(pre=[lint, test, build])
def deploy(c, environment="staging"):
    """Deploy the project to the selected environment"""
    if environment == "production":
        c.run("twine upload dist/*")
    else:
        print(f"Deploying to {environment}")

Working with Docker

@task
def docker_build(c, tag="latest"):
    """Build a Docker image"""
    c.run(f"docker build -t myapp:{tag} .")

@task
def docker_run(c, port=8000):
    """Run a Docker container"""
    c.run(f"docker run -p {port}:8000 myapp:latest")

@task
def docker_push(c, tag="latest"):
    """Push the Docker image to a registry"""
    c.run(f"docker push myregistry/myapp:{tag}")

Database Management

@task
def db_migrate(c):
    """Apply database migrations"""
    c.run("python manage.py migrate")

@task
def db_seed(c):
    """Load seed data into the database"""
    c.run("python manage.py loaddata fixtures/initial_data.json")

@task
def db_backup(c):
    """Create a timestamped database backup"""
    from datetime import datetime
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    c.run(f"pg_dump mydb > backup_{timestamp}.sql")

Integration with Other Tools

Git Operations

@task
def git_status(c):
    """Show repository status"""
    result = c.run("git status --porcelain", hide=True)
    if result.stdout:
        print("Uncommitted changes detected")
    else:
        print("Working directory clean")

@task
def release(c, version):
    """Create a new release tag and push it"""
    c.run(f"git tag v{version}")
    c.run("git push origin main")
    c.run(f"git push origin v{version}")

Virtual Environment Integration

@task
def venv_create(c):
    """Create a Python virtual environment"""
    c.run("python -m venv venv")
    print("Virtual environment created")

@task
def venv_install(c):
    """Install project dependencies inside the virtual environment"""
    with c.prefix("source venv/bin/activate"):
        c.run("pip install -r requirements.txt")

Debugging and Monitoring

Task Logging

import logging
from invoke import task

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@task
def logged_task(c):
    """Task with structured logging"""
    logger.info("Task start")
    result = c.run("echo 'Hello'", hide=True)
    logger.info(f"Result: {result.stdout.strip()}")
    logger.info("Task completed")

Measuring Execution Time

import time
from invoke import task

@task
def timed_task(c):
    """Measure task execution duration"""
    start_time = time.time()
    c.run("sleep 2")
    end_time = time.time()
    print(f"Execution time: {end_time - start_time:.2f} seconds")

Error Handling and Exceptions

Command Failure Handling

@task
def safe_task(c):
    """Task with robust error handling"""
    try:
        result = c.run("some_command_that_might_fail", warn=True)
        if not result.ok:
            print(f"Command exited with code {result.return_code}")
            print(f"Error output: {result.stderr}")
    except Exception as e:
        print(f"Exception occurred: {e}")

Rollback on Failure

@task
def deploy_with_rollback(c):
    """Deploy with automatic rollback on error"""
    # Save current state
    c.run("git stash")
    
    try:
        c.run("git pull")
        c.run("pip install -r requirements.txt")
        c.run("python manage.py migrate")
        c.run("systemctl restart myapp")
    except Exception as e:
        print(f"Deployment error: {e}")
        print("Rolling back...")
        c.run("git stash pop")
        c.run("systemctl restart myapp")
        raise

Performance Optimization

Parallel Task Execution

import concurrent.futures
from invoke import task

@task
def parallel_tests(c):
    """Run test suites in parallel to speed up CI"""
    test_commands = [
        "python -m pytest tests/unit/",
        "python -m pytest tests/integration/",
        "python -m pytest tests/functional/"
    ]
    
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [executor.submit(c.run, cmd) for cmd in test_commands]
        results = [f.result() for f in futures]
    
    print("All tests completed")

Caching Results

from functools import lru_cache
from invoke import task

@lru_cache(maxsize=1)
def get_git_commit():
    """Retrieve the current Git commit hash with caching"""
    import subprocess
    result = subprocess.run(['git', 'rev-parse', 'HEAD'],
                          capture_output=True, text=True)
    return result.stdout.strip()

@task
def show_version(c):
    """Display the current project version based on Git commit"""
    commit = get_git_commit()
    print(f"Current commit: {commit[:8]}")

Comparison with Alternatives

Tool Language CLI Support Learning Curve Typical Use Cases
Invoke Python Full Low Automation scripts, DevOps pipelines
Makefile DSL Limited Medium Project builds, compilation
Fabric Python Full Medium SSH automation, remote deployment
Bash Shell Full Medium System administration
Gradle Groovy/Kotlin Full High Java project builds
Gulp JavaScript Full Medium Frontend asset pipelines

Best Practices

Project Structure

project/
├── tasks/
│   ├── __init__.py
│   ├── build.py
│   ├── test.py
│   └── deploy.py
├── tasks.py
├── invoke.yaml
└── requirements.txt

Organizing Tasks

# tasks.py
from invoke import Collection
from tasks import build, test, deploy

ns = Collection()
ns.add_collection(build)
ns.add_collection(test)
ns.add_collection(deploy)

Documenting Tasks

@task(help={
    'environment': 'Target deployment environment (staging/production)',
    'version': 'Version identifier for the release',
    'dry_run': 'Simulate deployment without making changes'
})
def deploy(c, environment="staging", version="latest", dry_run=False):
    """
    Deploy the application to the specified environment.
    
    Execution flow:
    1. Update source code
    2. Install dependencies
    3. Apply database migrations
    4. Restart services
    """
    if dry_run:
        print(f"[DRY RUN] Deploy {version} to {environment}")
    else:
        print(f"Deploy {version} to {environment}")

Frequently Asked Questions

How do I pass arguments with default values?

Define function parameters with defaults:

@task
def deploy(c, environment="development", dry_run=False):
    print(f"Deploying to {environment}, dry_run={dry_run}")

How can I create a default task?

Use the default=True flag in the decorator:

@task(default=True)
def help(c):
    print("Help for available commands")

How do I hide command output?

Apply the hide parameter:

@task
def silent_task(c):
    result = c.run("ls -la", hide=True)
    print(f"Found files: {len(result.stdout.splitlines())}")

How can I handle command execution errors?

Set warn=True and inspect the ok attribute:

@task
def error_handling(c):
    result = c.run("false", warn=True)
    if not result.ok:
        print("Command failed")

How do I pass environment variables to a command?

Use the env parameter:

@task
def with_env(c):
    c.run("python script.py", env={"DEBUG": "1", "LOG_LEVEL": "info"})

How do I create an interactive task?

Enable a pseudo‑terminal with pty=True:

@task
def interactive(c):
    c.run("python manage.py shell", pty=True)

How can I organize tasks into sub‑groups?

Use collections to group related tasks:

from invoke import Collection

build_tasks = Collection('build')
build_tasks.add_task(clean)
build_tasks.add_task(compile)

ns = Collection()
ns.add_collection(build_tasks)

How do I access configuration inside a task?

Reference the config attribute of the Context object:

@task
def show_config(c):
    print(f"Project: {c.config.project.name}")
    print(f"Version: {c.config.project.version}")

Conclusion

Invoke is a robust, flexible tool for automating development workflows in Python projects. Its intuitive syntax, extensive configurability, and seamless integration with system commands make it an essential assistant for DevOps engineers, developers, and system administrators.

Key advantages of Invoke include a low learning curve, deep Python ecosystem integration, and the ability to construct complex automation pipelines. It excels at creating CLI utilities, automating deployments, managing infrastructure, and streamlining everyday development tasks.

Adopting Invoke dramatically simplifies routine operations, improves reliability, and establishes a consistent automation strategy across development teams.

News