πŸ“– Looking for karrio's legacy docs? Visit docs.karrio.io

Plugin Development

Karrio’s plugin system allows you to extend its functionality with custom carriers, address validators, and other integrations. This guide explains how to develop and register plugins with Karrio.

Plugin Types

Karrio supports several types of plugins:

  1. Carrier Integrations: Connect with shipping carriers like UPS, FedEx, DHL, etc.
  2. Address Validation: Add support for address validation services
  3. Duty & Tax Calculation: Add support for duty & tax calculation services (design pending)
  4. Package Insurances: Add support for package insurance services (design pending)
  5. Payment Services: Add support for payment services (design pending)

Plugin Development Approaches

Karrio offers two approaches for developing and registering plugins:

  1. Directory-based plugins: Traditional method where plugins are placed in a designated directory
  2. Entrypoint-based plugins: Modern method where plugins are registered via Python package entrypoints

Directory-Based Plugins

Directory-based plugins are loaded from local directories and don’t require a full Python package structure.

Directory Structure

A valid plugin directory must follow this structure:

1/your_plugin_directory/ 2β”œβ”€β”€ plugin_name/ 3β”‚ └── karrio/ 4β”‚ β”œβ”€β”€ plugins/ # For plugins (carrier integrations, address validators, etc...) 5β”‚ β”‚ └── plugin_name/ 6β”‚ β”‚ └── __init__.py # Contains METADATA 7β”‚ β”œβ”€β”€ mappers/ # For carrier integrations 8β”‚ β”‚ └── plugin_name/ 9β”‚ β”‚ β”œβ”€β”€ __init__.py # Contains METADATA 10β”‚ β”‚ β”œβ”€β”€ mapper.py # Implementation of the mapper 11β”‚ β”‚ β”œβ”€β”€ proxy.py # Implementation of the proxy 12β”‚ β”‚ └── settings.py # Settings schema 13β”‚ β”œβ”€β”€ validators/ # For address validators 14β”‚ β”‚ └── plugin_name/ 15β”‚ β”‚ β”œβ”€β”€ __init__.py # Contains METADATA 16β”‚ β”‚ └── validator.py # Implementation of the validator 17β”‚ β”œβ”€β”€ providers/ # Provider-specific implementations 18β”‚ β”‚ └── plugin_name/ 19β”‚ β”‚ β”œβ”€β”€ __init__.py 20β”‚ β”‚ β”œβ”€β”€ error.py # Error handling 21β”‚ β”‚ β”œβ”€β”€ units.py # Units and enums 22β”‚ β”‚ β”œβ”€β”€ utils.py # Utility functions 23β”‚ β”‚ └── other implementation files... 24β”‚ └── schemas/ # API schema definitions 25β”‚ └── plugin_name/ 26β”‚ β”œβ”€β”€ __init__.py 27β”‚ └── various schema files...

Loading Directory-Based Plugins

There are several ways to register directory-based plugins with Karrio:

  1. Default plugins directory:

    • Place your plugin in a plugins directory in the current working directory
  2. Custom plugin directory via environment variable:

    • Set the KARRIO_PLUGINS environment variable to the path of your plugins directory:
    1export KARRIO_PLUGINS=/path/to/your/plugins
  3. Programmatically add plugin directories:

    1import karrio.plugins as plugins 2plugins.add_plugin_directory('/path/to/your/plugins')

Entrypoint-Based Plugins (New)

Entrypoint-based plugins use Python’s setuptools entrypoint mechanism to register plugins with Karrio. This approach allows for easier distribution and installation of plugins as Python packages.

Creating an Entrypoint-Based Plugin

  1. Create a Python package with your plugin code following the same structure as directory-based plugins

  2. Register your plugin as an entrypoint in your package’s pyproject.toml or setup.py:

    For pyproject.toml:

    1[project.entry-points."karrio.plugins"] 2my_plugin_name = "my_package.module:METADATA"

    For setup.py:

    1setup( 2 name="my-karrio-plugin", 3 # ... other setup parameters ... 4 entry_points={ 5 "karrio.plugins": [ 6 "my_plugin_name = my_package.module:METADATA", 7 ], 8 }, 9)
  3. Install your package using pip:

    1pip install -e .

Karrio will automatically discover and load your plugin when it starts.

Plugin Discovery and Loading

Karrio dynamically discovers and loads plugins using a structured approach:

  1. Discovery: Scans configured directories for plugin structures
  2. Module Import: Imports modules based on their types (plugins, mappers, validators)
  3. Metadata Collection: Extracts METADATA from each plugin’s __init__.py file
  4. Namespace Extension: Extends the Karrio namespace with plugin modules

This dynamic loading system allows for hot-swapping plugins and modular integrations without modifying the core codebase.

Plugin Metadata

Every plugin must define a METADATA object in its module to provide information about the plugin’s capabilities and features.

Example Metadata for a Carrier Integration

1from karrio.core.metadata import PluginMetadata 2 3# Import local plugin components 4from .mapper import Mapper 5from .proxy import Proxy 6from .settings import Settings 7from . import units 8 9# Define plugin metadata 10METADATA = PluginMetadata( 11 # Plugin identification 12 id="my_carrier", 13 label="My Carrier Service", 14 status="beta", 15 16 # Plugin components 17 Mapper=Mapper, 18 Proxy=Proxy, 19 Settings=Settings, 20 21 # Additional metadata 22 description="Integration with My Carrier shipping service", 23 website="https://www.mycarrier.com/", 24 documentation="https://docs.mycarrier.com/", 25 26 # Service definitions 27 services=units.ShippingService, 28 options=units.ShippingOption, 29 package_presets=units.PackagePresets, 30)

Example Metadata for an Address Validator

1from karrio.core.metadata import PluginMetadata 2 3# Import local plugin components 4from .validator import Validator 5from .settings import Settings 6 7# Define plugin metadata 8METADATA = PluginMetadata( 9 # Plugin identification 10 id="my_validator", 11 label="My Address Validator", 12 status="beta", 13 14 # Plugin components 15 Validator=Validator, 16 Settings=Settings, 17 18 # Additional metadata 19 description="Address validation service", 20 website="https://www.myvalidator.com/", 21 documentation="https://docs.myvalidator.com/", 22)

Plugin Registration Precedence

When multiple plugins with the same name/ID are registered through different methods, Karrio follows this precedence order:

  1. Entrypoint-based plugins (highest precedence)
  2. Directory-based plugins from custom directories
  3. Directory-based plugins from the default directory

Testing Your Plugin

To test your plugin during development:

  1. For directory-based plugins:

    • Place your plugin in the plugins directory or use KARRIO_PLUGINS environment variable
    • Restart Karrio server
  2. For entrypoint-based plugins:

    • Install your package in development mode: pip install -e .
    • Restart Karrio server

You can verify that your plugin was loaded by checking the logs or using the Karrio admin interface.

Troubleshooting

If your plugin isn’t loading correctly:

  1. Check Karrio logs for error messages
  2. Ensure your plugin structure follows the required format
  3. Verify that the METADATA object is correctly defined
  4. For entrypoint-based plugins, make sure your package is correctly installed

Best Practices

  1. Version compatibility: Include Karrio version compatibility information in your plugin metadata
  2. Error handling: Implement proper error handling to prevent plugin failures from affecting core functionality
  3. Documentation: Document your plugin’s features, configuration options, and usage instructions
  4. Testing: Create comprehensive tests for your plugin to ensure it works correctly
  5. Maintenance: Keep your plugin updated with the latest Karrio API changes

Example Plugins

For reference implementations, see the Karrio Plugins Repository which contains example plugins using both directory-based and entrypoint-based approaches.

Plugin Distribution

For entrypoint-based plugins, you can distribute your plugin as a Python package through:

  1. PyPI: Upload your package to the Python Package Index
  2. Git repository: Allow users to install directly from your repository
  3. Wheel/ZIP files: Distribute packaged versions for manual installation

Additional Resources