Skip to main content

A pure tag system for parameter schemas in tools, pipelines, and agents

Project description

Iporter

A pure tag system for parameter schemas in tools, pipelines, and agents. Define structures, validate values, match providers, and auto-batch arrays—all with a concise, Zod-inspired API and zero dependencies.

Installation

pip install iporter

Quick Start

import iporter as p

# Define tags
user_tag = p.object({
    "name": p.string(min_length=1, max_length=100),
    "age": p.number().int().min(0).max(150),
    "email": p.string(),
    "role": p.enum(["admin", "user", "guest"]),
})

# Assign values
user_tag.assign({
    "name": "John Doe",
    "age": 30,
    "email": "john@example.com",
    "role": "admin"
})

# Match tags
required = p.object({"name": p.string(), "age": p.number()})
provided = p.object({"name": p.string(), "age": p.number()})
result = p.match_tags(required, provided)
print(result.is_compatible)  # True

# Auto-batching with Porter
required = p.object({"x": p.number(), "y": p.string()})
provA = p.object({"x": p.array(p.number()), "y": p.string()})
provA.assign({"x": [1, 2, 3], "y": "hello"})

porter = p.Porter(required)
porter.add_provides(provA)
porter.assign()
print(required.expanded_values)
# [{"x": 1, "y": "hello"}, {"x": 2, "y": "hello"}, {"x": 3, "y": "hello"}]

Tag Types

Type Description
StringTag String values with length constraints
NumberTag Numeric values (int/float) with min/max constraints
BooleanTag Boolean values
NullTag Null/None values
DateTimeTag DateTime values
ArrayTag Array/list values with item type constraints
ObjectTag Object/dict values with field definitions
UnionTag Union types (multiple possible types)
EnumTag Enum values (restricted set of values)
OptionalTag Optional values (allows None)
NullableTag Nullable values (allows None)
LiteralTag Literal values (exact match)
FileTag File values with extension/size/MIME type constraints

Tag Assignment

tag = p.string()
tag.assign("hello")
print(tag.has_value)  # True
print(tag.value)      # "hello"

# Assignment validates type
tag.assign(123)  # Raises ValueError

Default Values

# Constructor syntax
tag = p.string(default="hello")
print(tag.value)      # "hello"
print(tag.has_value)  # True
print(tag.is_default) # True

# Chainable syntax
tag = p.string().default("hello")

# Explicit assign overrides default
tag.assign("world")
print(tag.is_default) # False

Custom Validation

# Add custom validation function
def check_non_empty(v):
    if len(v) == 0:
        raise ValueError("Cannot be empty")

tag = p.string().validate(check_non_empty)
tag.assign("hello")  # Passes
tag.assign("")       # Raises ValueError: Cannot be empty

# Chainable with other methods
tag = p.string().name("email").validate(lambda v: "@" in v or ValueError("Invalid email"))
tag.assign("test@example.com")  # Passes

Tag Matching

required = p.object({"name": p.string(), "age": p.number()})
provided = p.object({"name": p.string(), "age": p.number(), "email": p.string()})

result = p.match_tags(required, provided)
print(result.is_compatible)  # True
print(result.extra_fields)   # ["email"]

Field Aliases

# Register aliases for fields with same meaning but different names
p.register_alias("email", "email_address")
p.register_alias("email", "user_email")

# Or batch register
p.register_aliases({
    "email": ["email_address", "user_email", "e_mail"],
    "name": ["username", "user_name"],
})

# Matching automatically considers aliases
required = p.object({"email": p.string()})
provided = p.object({"email_address": p.string()})

result = p.match_tags(required, provided)
print(result.is_compatible)  # True

# Resolve alias to canonical name
print(p.resolve_alias("email_address"))  # "email"
print(p.get_aliases("email"))  # ["email_address", "user_email"]

# Clear all aliases
p.clear_aliases()

Porter Orchestration

# Define tags
required = p.object({"x": p.number(), "y": p.string()})
provA = p.object({"x": p.array(p.number()), "y": p.string()})
provB = p.object({"y": p.array(p.string())})

# Assign values to providers
provA.assign({"x": [1, 2], "y": "hello"})
provB.assign({"y": ["a", "b"]})

# Create Porter and configure
porter = p.Porter(required)
porter.add_provides(provA, provB, mode="product")

# Assign values from providers to required
porter.assign()
print(required.expanded_values)
# 6 items (Cartesian product)

File Tags

# Basic file tag
tag = p.file()
tag.validate_type("/path/to/file.txt")  # True

# With constraints
tag = p.file(
    extensions=[".json", ".csv"],
    max_size=1024 * 1024,  # 1MB
    mime_types=["application/json", "text/csv"],
)

# Validate file path
tag.validate_type("/path/to/data.json")  # True
tag.validate_type("/path/to/image.png")  # False

API Reference

Tag Builders

Builder Description
p.string(min_length, max_length, name, description, default) String tag
p.number(is_int, min_val, max_val, name, description, default) Number tag
p.boolean(name, description, default) Boolean tag
p.null(name, description) Null tag
p.datetime(name, description, default) DateTime tag
p.array(item_tag, name, description, default) Array tag
p.object(shape, name, description, default) Object tag
p.union(tags, name, description) Union tag
p.enum(values, name, description, default) Enum tag
p.optional(inner_tag, name, description) Optional tag
p.nullable(inner_tag, name, description) Nullable tag
p.literal(value, name, description) Literal tag
p.file(extensions, max_size, mime_types, name, description) File tag

Tag Methods

Method Description
tag.assign(value) Assign a value (validates type + custom validator)
tag.validate_type(value) Check if value matches tag type
tag.type_name() Get type name string
tag.name(value) Set tag name (chainable)
tag.description(desc) Set description (chainable)
tag.default(value) Set default value (chainable)
tag.validate(fn) Set custom validation function (chainable)
tag.get_info() Get name and description as dict
tag.value Get assigned value (or default)
tag.has_value Check if value assigned (or has default)
tag.is_default Check if current value is from default

ObjectTag Properties

Property Description
tag.shape Get field definitions
tag.required_fields Get required field names
tag.optional_fields Get optional field names
tag.expanded_values Get expanded values (after Porter batching)

Porter

Method/Property Description
p.Porter(required) Create Porter with required tag
porter.add_provides(*providers, mode=None, dup_strategy=None) Add provider tags
porter.assign() Assign values from providers to required tag
porter.required Get the required tag
porter.providers Get provider tags dict
porter.batch_mode Get/set batch mode: "zip" (default) or "product"
porter.dup_strategy Get/set duplicate key strategy: "merge" (default), "first", "last", or "error"

Matching and Batching

Function Description
p.match_tags(required, provided) Match two tags
p.match_tags_multi(required, provided_list) Match against multiple providers
p.auto_batch(required, providers, values, mode, dup_strategy) Auto-batch from providers

Aliases

Function Description
p.register_alias(canonical, alias) Register a field alias
p.register_aliases(alias_map) Register multiple aliases
p.resolve_alias(name) Resolve alias to canonical name
p.get_aliases(canonical) Get all aliases for a name
p.clear_aliases() Clear all aliases

Architecture

See ARCHITECTURE.md for detailed architecture documentation.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

iporter-0.1.0.tar.gz (22.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

iporter-0.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file iporter-0.1.0.tar.gz.

File metadata

  • Download URL: iporter-0.1.0.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for iporter-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fc667953781d50c08823c69525d30b8766a455e278ebf2d2d3f67609be4a2853
MD5 7577b2b2970fd0001f5919a9c055dd48
BLAKE2b-256 e87c3a28a4d2fac22a77ea540977bd11e9572b48bc84c8e8a0b135f98d6d97cb

See more details on using hashes here.

File details

Details for the file iporter-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: iporter-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for iporter-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38441276f14ded03d9d8f5247c9d3290a81ce27b872aea988710ad1289bc665c
MD5 085e978a81eeee4a698f911b330cb3e6
BLAKE2b-256 3051d7f4ba016f6c614f530fc7b9b2232dba5466b9213d1a183f7764839328c2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page