Python API Reference¶
This page documents the supported Python API. Internal modules are not a supported interface. To extend Portolan, follow the plugin guide and use the APIs on this page.
Top-level exports¶
Catalog
¶
A Portolan catalog backed by a .portolan directory.
The Catalog class provides the Python API for all catalog operations. The CLI commands are thin wrappers around these methods.
Note: This is the legacy v1 API. New code should use init_catalog() which creates the v2 file structure with catalog.json at root level.
Attributes:
| Name | Type | Description |
|---|---|---|
root |
The root directory containing the .portolan folder. |
portolan_path
property
¶
Path to the .portolan directory.
catalog_file
property
¶
Path to the catalog.json file (at root, not inside .portolan).
__init__(root)
¶
Initialize a Catalog instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Path
|
The root directory containing the .portolan folder. |
required |
init(root, *, license_id=None)
classmethod
¶
Initialize a new Portolan catalog.
Creates the catalog using the v2 file structure via init_catalog().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Path
|
The directory where the catalog should be created. |
required |
license_id
|
str | None
|
SPDX identifier to seed into metadata.yaml. Left None, the
catalog starts unlicensed and |
None
|
Returns:
| Type | Description |
|---|---|
Self
|
A Catalog instance for the newly created catalog. |
Raises:
| Type | Description |
|---|---|
CatalogExistsError
|
If a .portolan directory already exists. |
CatalogExistsError
¶
Bases: Exception
Raised when attempting to initialize a catalog that already exists.
Legacy exception kept for backward compatibility. New code should use CatalogAlreadyExistsError from portolan_cli.errors.
cli
¶
Portolan CLI - Command-line interface for managing cloud-native geospatial data.
The CLI is a thin wrapper around the Python API (see catalog.py). All business logic lives in the library; the CLI handles user interaction.
click_writer(prefix, message, *, fg, dim, file, nl)
¶
Write an output message through click.echo.
click.echo strips color from a pipe, follows the CliRunner color
setting, and wraps a Windows console. The library writer does not.
show_warning(message, category, filename, lineno, file=None, line=None)
¶
Print a PluginWarning as an output warning. Pass other warnings on.
A plugin warning names the plugin and the problem. The source path and line that Python adds point into Portolan, which does not help the user.
should_output_json(ctx, json_flag=False)
¶
Determine if JSON output should be used.
Checks both the global --format option and per-command --json flags. Global --format=json takes precedence, but per-command flags also work for backward compatibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
Context
|
Click context containing the format preference. |
required |
json_flag
|
bool
|
Per-command --json flag value. |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if JSON output should be used, False for text output. |
output_json_envelope(envelope)
¶
Output a JSON envelope to stdout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
envelope
|
Any
|
OutputEnvelope instance to output. |
required |
load_dotenv_and_warn_sensitive(catalog_path)
¶
Load .env from catalog and warn if config.yaml has sensitive settings.
This should be called after the actual catalog path is resolved (not in the global CLI callback) to ensure the correct .env is loaded when --catalog/--portolan-dir overrides the default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog_path
|
Path
|
Resolved catalog root path. |
required |
require_catalog_root(use_json=False, command_name='command')
¶
Find and validate catalog root, or exit with git-style error.
Git-style behavior: walks up from cwd to find .portolan/config.yaml. Commands that need to operate on the entire catalog use this to find the root regardless of which subdirectory they're run from.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
use_json
|
bool
|
If True, output error as JSON envelope. |
False
|
command_name
|
str
|
Name of the command for error messages. |
'command'
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to catalog root. |
Raises:
| Type | Description |
|---|---|
SystemExit
|
If not inside a catalog. |
resolve_remote(destination, catalog_path, collection=None)
¶
Resolve remote destination with precedence: CLI > env var > config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
destination
|
str | None
|
CLI-provided destination value (None if not specified). |
required |
catalog_path
|
Path | None
|
Path to catalog root for config lookup. |
required |
collection
|
str | None
|
Optional collection name for collection-level config. |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
Resolved destination URL or None if not configured. |
resolve_aws_profile(profile, catalog_path, collection=None)
¶
Resolve the AWS profile: CLI, then env var, then config.
A Portolan setting wins, because a user writes it for this tool. None means
that nothing here names a profile. The sync layer then reads AWS_PROFILE,
and falls back to the default profile, as boto3 and the AWS CLI do.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile
|
str | None
|
CLI-provided profile value (None if not specified). |
required |
catalog_path
|
Path | None
|
Path to catalog root for config lookup. |
required |
collection
|
str | None
|
Optional collection name for collection-level config. |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
The profile name, or None when nothing here names one. |
resolve_aws_region(region, catalog_path, collection=None)
¶
Resolve AWS region with precedence: CLI > env var > config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
region
|
str | None
|
CLI-provided region value (None if not specified). |
required |
catalog_path
|
Path | None
|
Path to catalog root for config lookup. |
required |
collection
|
str | None
|
Optional collection name for collection-level config. |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
Resolved region name or None if not configured. |
cli(ctx, output_format)
¶
Portolan - Publish and manage cloud-native geospatial data catalogs.
init(ctx, path, json_output, auto_mode, catalog_id, title, description, backend, license_id, license_url, logo, logo_title)
¶
Initialize a new Portolan catalog.
Creates a catalog.json at the root level and a .portolan directory with management files (config.yaml, metadata.yaml). Also creates versions.json at the root.
Derives the catalog ID from the directory name. Pass --id to set it yourself. The ID is the catalog's public STAC identity, so a directory named for a step in your workflow makes a poor one.
PATH is the directory where the catalog should be created (default: current directory).
A license is required. Every collection inherits it from the catalog's metadata.yaml, and 'portolan add' refuses to write a collection without one. Pass an SPDX identifier, or 'other' with --license-url when the license has no identifier. Without --auto or --json you are prompted for it.
Use --auto to skip all prompts and use default values. Use --id, --title, and --description to set catalog metadata directly.
A logo is optional. Pass --logo with a local image to copy it into _assets/ and publish it as a rel="icon" link, or add one later with 'portolan logo'.
\b Examples: portolan init # Prompts for the license portolan init --auto --license CC-BY-4.0 # Skip prompts portolan init --title "My Catalog" --license MIT portolan init --id phl-housing --license CC-BY-4.0 portolan init --license other --license-url https://x.org/terms portolan init --backend iceberg --license CC0-1.0 portolan init --auto --license CC-BY-4.0 --logo brand.png
list_cmd(ctx, collection, catalog_path, json_output, tracked_only, untracked_only)
¶
List all files in the catalog with tracking status.
Git-style behavior: automatically finds the catalog root by walking up from the current directory. Works from any subdirectory within a catalog. Use --catalog to override and specify an explicit path.
Shows all files organized by collection in a hierarchical tree view. Each file shows its tracking status, format type, and file size.
\b Status indicators: + = tracked (in versions.json, unchanged) + = untracked (on disk, not in versions.json) ~ = modified (in versions.json, checksum changed) ! = deleted (in versions.json, missing from disk)
\b Example output: censo-2010/ data/ (3 tracked, 2 untracked) + census-data.parquet (GeoParquet, 4.5MB) + metadata.parquet (GeoParquet, 1.2MB) + README.md (2KB) + style.json (1KB)
\b Examples: portolan list # List all files with status portolan list --collection demographics # Filter by collection portolan list --tracked-only # Show only tracked files portolan list --untracked-only # Show only untracked files portolan list --json # JSON output
status_cmd(ctx, collection, catalog_path, offline, json_output)
¶
Show local vs remote version state for collections.
Git-style status showing version sync state, modified files, and untracked files for each collection in the catalog.
\b Status information: Local version Current version in local versions.json Remote version Current version on remote (unless --offline) Sync state in_sync, ahead, behind, or unknown Modified Files changed since last version Untracked Files on disk not in versions.json Deleted Files in versions.json but missing from disk
\b Examples: portolan status # Status for all collections portolan status -c demographics # Status for one collection portolan status --offline # Skip remote check portolan status --json # JSON output for agents
info_cmd(ctx, target, catalog_path, json_output)
¶
Show information about a file, collection, or catalog.
TARGET can be: - A file path (e.g., demographics/census.parquet) - shows file metadata - A collection directory (e.g., demographics/) - shows collection metadata - Omitted - shows catalog-level metadata
The output format for files is
Format: GeoParquet CRS: EPSG:4326 Bbox: [-122.5, 37.7, -122.3, 37.9] Features: 4,231 Version: v1.2.0
\b Examples: portolan info demographics/census.parquet # File info portolan info demographics/ # Collection info portolan info # Catalog info portolan info demographics/census.parquet --json # JSON output
check(ctx, path, json_output, verbose, fix, dry_run, remove_legacy, force, workers, metadata, geo_assets, strict, live, public_url, no_data, data_scope, no_structural, schema)
¶
Validate a Portolan catalog against the Portolan spec.
Validation runs on rashid, which reports PTL-* rule ids citing the spec requirements they enforce. By default it checks metadata, STAC 1.1.0 structure, and the asset bytes themselves. The metadata and structural passes are offline. The data pass reads asset bytes, including remote https assets over HTTP range requests. Use --data-scope local to read only the assets inside the catalog tree. Use --no-data to skip the pass. With --fix, applies the mechanical repairs and re-checks.
The --live pass probes the published host for HTTP Range support and CORS headers. It also requests every link target under the base URL, and reports a linked file the host does not serve. It skips an upstream host only when it knows the base URL, from --url or publish.public_url. Without a base URL it probes every absolute href as declared, which can report CORS failures against a host you do not control.
PATH is the directory to check (default: current directory).
Use --metadata or --geo-assets to limit scope: - --metadata: Only validate the catalog (its own metadata, structure, data) - --geo-assets: Only check source files on disk for cloud-native status - Neither: Both (default)
Examples:
portolan check # Validate all (metadata + geo-assets)
portolan check --metadata # Validate the catalog only
portolan check --geo-assets # Check source files only
portolan check --data-scope local # Read only assets inside the catalog tree
portolan check --no-data # Skip reading asset bytes (faster)
portolan check --live # Also probe the published host
portolan check --fix # Fix what can be fixed, then re-check
portolan check --fix --dry-run # Preview all fixes
scan(ctx, path, json_output, no_recursive, max_depth, include_hidden, follow_symlinks, show_all, show_tree, suggest_collections, manual_only, fix, dry_run, strict)
¶
Scan a directory for geospatial files and potential issues.
Discovers files by extension, validates shapefile completeness, and reports issues that may cause problems during import.
PATH is the directory to scan (default: current directory).
\b Fix Mode: Use --fix to auto-rename files with: - Invalid characters (spaces, parentheses, non-ASCII) - Windows reserved names (CON, PRN, AUX, etc.) - Long paths (> 200 characters)
Use --dry-run to preview changes without applying.
Examples:
portolan scan # Scan current directory
portolan scan --json # JSON output in current directory
portolan scan /data/geospatial
portolan scan /large/tree --max-depth=2
portolan scan /data --no-recursive
portolan scan /data --fix --dry-run
portolan scan /data --fix
add_cmd(ctx, json_output, paths, verbose, item_id, catalog_path, item_datetime, workers, generate_parquet, generate_pmtiles, force_pmtiles, generate_thumbnails, force_thumbnails, force, reconvert, merge_strategy)
¶
Track files in the catalog.
Accepts multiple paths like git add. Each path is processed independently with automatic collection inference based on directory structure.
Works like git: run from anywhere inside a catalog and it auto-detects the catalog root. Use --portolan-dir to override.
\b Item ID derivation: By default, the item ID is derived from the parent directory name. For example, adding 'census/2020/data.parquet' creates an item named '2020'. Use --item-id to override this automatic derivation. All other files in the item directory are tracked as companion assets.
\b Datetime handling: --datetime applies to ALL items added in this command. For items with different acquisition dates, run separate add commands:
portolan add census/2020/ --datetime 2020-04-01
portolan add census/2023/ --datetime 2023-04-01
If --datetime is omitted, items have a null temporal extent and carry
the sentinel start/end range. Run 'portolan check' to find items
needing dates.
\b Examples: portolan add demographics/census.parquet portolan add file1.geojson file2.geojson # Add multiple files portolan add imagery/ # Add all files in directory portolan add . # Add all files in catalog portolan add data.geojson --item-id my-id # Override item ID (single file only) portolan add sat.tif --datetime 2024-06-15 # Explicit acquisition date
Smart behavior: - Unchanged files are silently skipped (use --verbose to see them) - Changed files are re-extracted with new metadata - Sidecar files (.dbf, .shx, .prj for shapefiles) are auto-detected - All files in the item directory are tracked, not just geo files
\b Large file partitioning: GeoParquet files exceeding 2GB are automatically partitioned into spatial chunks using KD-tree partitioning. In interactive mode, you'll be prompted before partitioning. Configure via:
partitioning.enabled: true/false (default: true)
partitioning.prompt: true/false (default: true)
partitioning.threshold_gb: size in GB (default: 2.0)
add_external_cmd(ctx, json_output, url, collection_id, title, description, media_type, license_id, license_url, via_url, bbox, catalog_path, force)
¶
Register remote data as a collection WITHOUT downloading or converting it.
The external counterpart to 'portolan add'. Some valuable data sources are already published cloud-natively at a remote location and should be referenced in place rather than copied. This creates a collection.json whose collection-level 'data' asset href is the remote URL (kept as-is, carrying the 'external' role) plus a rel:via provenance link.
The remote asset is never fetched, and 'portolan check' will not try to convert it (the metadata scanner skips scheme-qualified hrefs).
A license is required. Pass an SPDX identifier, or 'other' with --license-url when the license has no identifier. Referencing someone else's data in place does not make its license unknowable, and a collection without one is an error under 'portolan check'.
\b Examples: # Overture Maps places — planet-scale GeoParquet on Overture's S3 portolan add-external \ "s3://overturemaps-us-west-2/release/2024-09-18.0/theme=places/type=place/*" \ --collection overture-places \ --title "Overture Maps — Places" \ --license ODbL-1.0 \ --via "https://docs.overturemaps.org/guides/places/"
portolan add-external "https://example.org/data/buildings.parquet" \\
--license other --license-url "https://example.org/terms"
rm_cmd(ctx, json_output, path, keep, force, dry_run, verbose, catalog_path)
¶
Remove files from tracking.
By default, removes the file from disk AND untracks it from the catalog. Requires --force for destructive operations (deleting files).
Works like git: run from anywhere inside a catalog and it auto-detects the catalog root. Use --portolan-dir to override.
A file that is already gone from disk is still untracked, so you can drop a tracked asset that you deleted by hand. The removal is recorded as a new version.
\b Safety flags: - --keep: Untrack file but preserve it on disk (safe, no --force needed) - --force: Required for destructive rm (when not using --keep) - --dry-run: Preview what would be removed without actually removing
\b Examples: portolan rm --keep imagery/old_data.tif # Safe: untrack only portolan rm --dry-run vectors/ # Preview what would be removed portolan rm -f demographics/census.parquet # Force delete and untrack portolan rm -f vectors/ # Force remove entire directory
push(ctx, json_output, verbose, destination, collection, force, dry_run, profile, catalog_path, workers, concurrency, chunk_concurrency, max_connections, adaptive)
¶
Push local catalog changes to cloud object storage.
Git-style behavior: automatically finds the catalog root by walking up from the current directory. Works from any subdirectory within a catalog. Use --catalog to override and specify an explicit path.
Syncs collection(s) to a remote destination (S3, GCS, Azure). Uses optimistic locking to detect concurrent modifications.
DESTINATION is the object store URL (e.g., s3://mybucket/my-catalog). If not provided, uses 'remote' from PORTOLAN_REMOTE env var or .env file.
If --collection is specified, pushes that collection only. If --collection is omitted, pushes all collections in the catalog.
\b Examples: # Push a single collection portolan push s3://mybucket/catalog --collection demographics portolan push gs://mybucket/catalog -c imagery --dry-run
# Push all collections
portolan push s3://mybucket/catalog
portolan push --dry-run # Uses configured remote
pull_command(ctx, json_output, remote_url, collection, catalog_path, force, dry_run, restore, profile, workers, concurrency)
¶
Pull updates from a remote catalog.
Git-style behavior: automatically finds the catalog root by walking up from the current directory. Works from any subdirectory within a catalog. Use --catalog to override and specify an explicit path.
Fetches changes from a remote catalog and downloads updated files.
Similar to git pull, this checks for uncommitted local changes before
overwriting.
REMOTE_URL is the remote catalog URL (e.g., s3://bucket/catalog).
If --collection is specified, pulls that collection only. If --collection is omitted, pulls all collections in the catalog.
\b Examples: # Pull a single collection portolan pull s3://mybucket/my-catalog --collection demographics portolan pull s3://mybucket/catalog -c imagery --dry-run
# Pull all collections
portolan pull s3://mybucket/catalog
portolan pull s3://mybucket/catalog --workers 4
sync(ctx, json_output, destination, collection, force, dry_run, fix, profile, catalog_path)
¶
Sync local catalog with remote storage (pull + push).
Orchestrates a full sync workflow: Pull -> Init -> Scan -> Check -> Push. This is the recommended way to keep a local catalog in sync with remote.
DESTINATION is the object store URL (e.g., s3://mybucket/my-catalog).
\b Examples: portolan sync s3://mybucket/catalog --collection demographics portolan sync s3://mybucket/catalog -c imagery --dry-run portolan sync s3://mybucket/catalog -c data --fix --force portolan sync s3://mybucket/catalog -c data --profile prod portolan sync --collection demographics # Uses configured remote
clone(ctx, json_output, remote_url, local_path, collection, profile)
¶
Clone a remote catalog to a local directory.
This is essentially "pull to an empty directory" with guardrails. Creates the target directory and pulls collections from remote storage.
REMOTE_URL is the object store URL (e.g., s3://mybucket/my-catalog).
LOCAL_PATH is optional - if not provided, it will be inferred from the catalog name in the URL (git clone style).
--collection is optional - if not provided, all collections in the remote catalog will be cloned.
\b Examples: # Infer directory from URL, clone all collections portolan clone s3://mybucket/my-catalog
# Clone to current directory (must be empty)
portolan clone s3://mybucket/my-catalog.
# Clone specific collection
portolan clone s3://mybucket/catalog -c demographics
# Clone all collections to specific directory
portolan clone s3://mybucket/catalog ./local-copy
# Clone specific collection with profile
portolan clone s3://mybucket/catalog ./data -c imagery --profile prod
config()
¶
Manage catalog configuration.
Configuration is stored in .portolan/config.yaml and follows this precedence:
\b
1. CLI argument (highest)
2. Environment variable (PORTOLAN_
Note: Sensitive settings (remote, profile, region) must use env vars or .env.
\b Examples: portolan config set backend iceberg portolan config get remote portolan config list
config_set(ctx, json_output, key, value, collection)
¶
Set a configuration value.
KEY is the setting name (e.g., backend, statistics.enabled). VALUE is the value to set.
Note: Sensitive settings (remote, profile, region) cannot be stored in config.yaml. Use environment variables or .env files instead.
\b Examples: portolan config set backend iceberg portolan config set statistics.enabled true portolan config set pmtiles.enabled false --collection demographics
config_get(ctx, json_output, key, collection)
¶
Get a configuration value.
Shows the resolved value and its source (env, catalog, collection, or not set).
KEY is the setting name (e.g., remote, aws_profile).
\b Examples: portolan config get remote portolan config get aws_profile --collection restricted
config_list(ctx, json_output, collection)
¶
List all configuration settings.
Shows all settings with their values and sources.
\b Examples: portolan config list portolan config list --collection demographics
config_unset(ctx, json_output, key, collection)
¶
Remove a configuration value.
Removes the setting from the config file. Does not affect environment variables.
KEY is the setting name to remove.
\b Examples: portolan config unset remote portolan config unset aws_profile --collection restricted
clean(ctx, json_output, dry_run)
¶
Remove all Portolan metadata while preserving data files.
Removes catalog.json, collection.json, item.json (STAC metadata), versions.json, and the .portolan/ directory. Preserves all data files (.parquet, .tif, .gpkg, .geojson, etc.).
Use --dry-run to preview what would be removed without deleting anything.
\b Examples: portolan clean # Remove all metadata portolan clean --dry-run # Preview what would be removed
metadata()
¶
Manage catalog metadata for README generation.
metadata.yaml files supplement STAC with human-enrichable fields like titles, descriptions, contact info, and citations. These files can exist at any level in the catalog hierarchy (catalog, subcatalog, collection).
\b Examples: portolan metadata init # Create template at catalog root portolan metadata init demographics # Create template for collection portolan metadata validate # Validate metadata.yaml
metadata_init(ctx, json_output, path, force, no_recursive)
¶
Generate a metadata.yaml template.
Creates .portolan/metadata.yaml files at all STAC levels (catalogs, subcatalogs, collections) by default. Skips items (item.json directories) and preserves existing files unless --force is used.
If PATH is provided, starts from that directory. Otherwise, starts at the catalog root.
\b Examples: portolan metadata init # All levels in catalog portolan metadata init climate # All levels under climate/ portolan metadata init --force # Overwrite existing portolan metadata init --no-recursive # Only at catalog root portolan metadata init demographics --no-recursive # Only for collection
metadata_validate(ctx, json_output, path, no_recursive)
¶
Validate metadata.yaml against schema.
Validates all .portolan/metadata.yaml files in the catalog tree by default. Checks for: - Required fields: contact (name + email), license - Format validation: email, SPDX license identifier, DOI
Uses hierarchical resolution: child metadata.yaml files inherit from parent levels and override specific fields.
\b Examples: portolan metadata validate # Validate all levels portolan metadata validate climate # Validate under climate/ portolan metadata validate --no-recursive # Only at catalog root portolan metadata validate demographics --no-recursive # Only for collection
readme(ctx, json_output, path, stdout, check, no_recursive, verbose)
¶
Generate README.md from STAC metadata and metadata.yaml.
Generates READMEs for the catalog and all collections by default. The README is a pure output - always generated from STAC (machine-extracted metadata) plus .portolan/metadata.yaml (human enrichment). Never hand-edit the README; edit metadata.yaml instead and regenerate.
Use --check in CI to verify READMEs are up-to-date:
\b Examples: portolan readme # Generate for catalog and all collections portolan readme climate # Generate under climate/ portolan readme --check # CI mode: exit 1 if any stale portolan readme --no-recursive # Only at catalog root portolan readme demographics --no-recursive # Only for collection portolan readme --stdout --no-recursive # Print single README
logo_cmd(ctx, source, title, json_output)
¶
Publish SOURCE as the catalog logo.
Copies the image to _assets/ beside the root catalog.json and writes a rel="icon" link pointing at it. A registry lists many catalogs side by side, and the logo is what makes yours recognizable.
SOURCE must be a local file in one of the seven permitted formats: APNG, AVIF, GIF, JPEG, PNG, SVG, or WebP. A URL is rejected — download the image first. SVG conforms but STAC Browser will not render it.
Re-run to replace the logo: the previous link and image are removed, so the catalog always carries exactly one.
\b Examples: portolan logo brand.png # Title from the catalog portolan logo brand.png --title "Acme GIS" # Explicit title
extract()
¶
Extract data from external sources into Portolan catalogs.
Convert data from ArcGIS services, APIs, or other sources into well-structured Portolan catalogs with STAC metadata.
\b Examples: portolan extract arcgis https://services.arcgis.com/.../FeatureServer ./output portolan extract arcgis URL --layers "Census" --dry-run portolan extract arcgis URL --filter "sdn_" --resume
partition(ctx, input_file, output_dir, strategy, target_rows, preview, verbose)
¶
Partition a large GeoParquet file for better query performance.
Splits a GeoParquet file into spatially-organized partitions using geoparquet-io. Per OGC best practices, files over 2GB should be partitioned.
\b Output structure (Hive-style): output_dir/ ├── kdtree_cell=001/ │ └── data.parquet ├── kdtree_cell=002/ │ └── data.parquet └── ...
\b Examples: # Preview partition strategy portolan partition buildings.parquet --preview
# Partition with default settings (kdtree, 120k rows/partition)
portolan partition buildings.parquet output/
# Custom target rows
portolan partition buildings.parquet output/ --target-rows 50000
version()
¶
Version management commands.
Works with any versioning backend (file, iceberg). Backend is auto-detected from catalog configuration.
\b Subcommands: current Show current version of a collection list List all versions of a collection rollback Rollback to a previous version (iceberg only) prune Remove old versions (iceberg only)
current(ctx, collection, catalog_path, json_output)
¶
Show the current version of a collection.
Works with any versioning backend (auto-detected from config).
\b Examples: portolan version current boundaries portolan version current boundaries --json
version_list_cmd(ctx, collection, catalog_path, json_output)
¶
List all versions of a collection.
Works with any versioning backend (auto-detected from config).
\b Examples: portolan version list boundaries portolan version list boundaries --json
bump(ctx, collection, new_version, notes, breaking, yes, catalog_path, json_output)
¶
Create a new version from current file state.
Detects modified files by comparing checksums, computes new checksums, and creates a new version entry in versions.json.
NEW_VERSION must be an explicit semver string (e.g., "1.2.0").
\b Examples: portolan version bump demographics 1.4.0 -m "Updated source data" portolan version bump demographics 2.0.0 --breaking -m "Schema change" portolan version bump demographics 1.4.0 -y # Skip confirmation
stac_geoparquet(ctx, collection, catalog_path, dry_run, json_output)
¶
Generate items.parquet for efficient STAC queries.
Creates a GeoParquet file containing all items in a collection, enabling fast spatial/temporal queries without N HTTP requests.
This is optional but recommended for collections with >100 items. The parquet file is added as a link in collection.json.
If --collection is omitted, generates for ALL collections in the catalog.
\b Examples: portolan stac-geoparquet # Generate for ALL collections portolan stac-geoparquet -c landsat # Generate for landsat collection portolan stac-geoparquet -c imagery --dry-run # Preview without creating portolan stac-geoparquet --json # JSON output for all collections
skills()
¶
List and view AI skills for Portolan workflows.
Skills have moved to: https://github.com/portolan-sdi/portolan-skills
skills_list_cmd(ctx, json_output)
¶
List available skills.
skills_show_cmd(ctx, name, json_output)
¶
View a skill.
detect_format(path, catalog_root=None)
¶
Return the format family of a file, asking the plugin format handlers first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the file or directory to detect. |
required |
catalog_root
|
Path | None
|
Catalog root, for a |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
The family of the handler that claims the file, for example |
str | None
|
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the file does not exist. |
IsADirectoryError
|
If the path is a non-geospatial directory. |
Plugin API¶
Import these names from portolan_cli.api. The module follows semantic
versioning.
Hooks¶
hookimpl = pluggy.HookimplMarker(PROJECT_NAME)
module-attribute
¶
EnrichContext
dataclass
¶
Where the STAC document that a hook enriches lives.
Attributes:
| Name | Type | Description |
|---|---|---|
catalog_root |
Path
|
Directory that holds |
collection_id |
str
|
Collection identifier, nested ids joined with |
document_path |
Path
|
Path of the item or collection JSON file. |
item_id |
str | None
|
Item identifier. None for a collection. |
Format handlers¶
FormatHandler
¶
Bases: Protocol
Convert one file format to a cloud-native file and read its metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Unique handler name, used by |
family |
str
|
Free text that groups formats, for example |
display_name |
str
|
Name for messages, for example |
extensions |
frozenset[str]
|
Lowercase source extensions with a dot, for example |
media_type |
str
|
Media type of the converted file. |
asset_level |
str
|
|
name
property
¶
Unique handler name.
family
property
¶
Format family.
display_name
property
¶
Name for messages.
extensions
property
¶
Source extensions.
media_type
property
¶
Media type of the converted file.
asset_level
property
¶
Either collection or item.
claims(path)
¶
Return True when this handler reads path. Read file contents if needed.
output_path(source, output_dir)
¶
Return where convert writes the cloud-native file for source.
add --force keeps an output that exists at this path.
convert(source, output_dir, catalog_root)
¶
Write the cloud-native file into output_dir and return its path.
Validate the result and raise InvalidOutputError when it fails.
Write a thumbnail here when the format has one. add and
check --fix both call this.
read_metadata(path)
¶
Read the metadata of a converted file.
describe_item(item, output, metadata, context)
¶
Add format fields to a new STAC item before Portolan saves it.
item is a pystac.Item. Change it in place: properties,
assets, and stac_extensions.
describe_collection(collection, metadata, context)
¶
Add format fields to a collection after an add.
collection is a pystac.Collection. metadata holds what
read_metadata returned for each file this add converted.
inspect(path, catalog_root)
¶
Return a FileInfo for portolan info. Raise ValueError if unsupported.
FormatMetadata
¶
FormatContext
dataclass
¶
Where a file sits, for the handler methods that describe STAC.
Attributes:
| Name | Type | Description |
|---|---|---|
catalog_root |
Path
|
Directory that holds |
collection_dir |
Path
|
Directory that holds |
source |
Path | None
|
The file the operator added. None for a collection. |
defaults |
dict[str, Any]
|
The |
merge_strategy |
Any
|
How |
Versioning backends¶
VersioningBackend
¶
Bases: Protocol
Protocol for version storage and management backends.
This protocol defines the interface that all versioning backends must implement. The MVP uses JsonFileBackend (versions.json), while enterprise deployments can use IcebergBackend via the portolake plugin for ACID transactions.
All methods take a collection parameter identifying which collection
to operate on. The collection path is relative to the catalog root.
Thread Safety
This protocol assumes single-writer access. Implementations are NOT required to be thread-safe. Use drift checking to detect concurrent modifications. For true concurrent access, use a backend with distributed locking (e.g., IcebergBackend via portolake).
get_current_version(collection)
¶
Get the current (latest) version of a collection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
Returns:
| Type | Description |
|---|---|
Version
|
The current Version object. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the collection has no versions. |
list_versions(collection)
¶
List all versions of a collection, oldest first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
Returns:
| Type | Description |
|---|---|
list[Version]
|
List of Version objects, ordered oldest to newest. |
publish(collection, assets, schema, breaking, message, removed=None, version=None)
¶
Publish a new version of a collection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
assets
|
dict[str, str]
|
Mapping of asset names to asset paths/URIs. |
required |
schema
|
SchemaFingerprint
|
Schema fingerprint for change detection. |
required |
breaking
|
bool
|
Whether this is a breaking change. |
required |
message
|
str
|
Human-readable description of the change. |
required |
removed
|
set[str] | None
|
Set of asset keys to remove from the version. |
None
|
version
|
str | None
|
Explicit version string. If None, auto-compute next version. |
None
|
Returns:
| Type | Description |
|---|---|
Version
|
The newly created Version object. |
rollback(collection, target_version)
¶
Rollback to a previous version.
This creates a NEW version with the contents of the target version, preserving full history. It does not delete intermediate versions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
target_version
|
str
|
Semantic version string to roll back to. |
required |
Returns:
| Type | Description |
|---|---|
Version
|
The newly created Version object (representing the rollback). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If target_version doesn't exist. |
prune(collection, keep, dry_run)
¶
Remove old versions, keeping the N most recent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
keep
|
int
|
Number of recent versions to keep. |
required |
dry_run
|
bool
|
If True, don't actually delete, just report what would be deleted. |
required |
Returns:
| Type | Description |
|---|---|
list[Version]
|
List of Version objects that were (or would be) deleted. |
check_drift(collection)
¶
Check for drift between local and remote state.
Detects if another process has modified the remote versions.json without going through this backend. Used to catch accidental concurrent access in the MVP single-writer model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
Collection identifier/path. |
required |
Returns:
| Type | Description |
|---|---|
DriftReport
|
DriftReport with drift status and details. |
DriftReport
¶
Bases: TypedDict
Report from drift detection between local and remote state.
Attributes:
| Name | Type | Description |
|---|---|---|
has_drift |
bool
|
True if local and remote versions differ. |
local_version |
str | None
|
Local version string, or None if no local version exists. |
remote_version |
str | None
|
Remote version string, or None if no remote version exists. |
message |
str
|
Human-readable description of the drift status. |
SchemaFingerprint
¶
Bases: TypedDict
Schema fingerprint for detecting breaking changes between versions.
Attributes:
| Name | Type | Description |
|---|---|---|
columns |
list[str]
|
Ordered list of column names. |
types |
dict[str, str]
|
Mapping of column names to their data types (e.g., "int64", "geometry"). |
hash |
str
|
Hash of the schema for quick comparison. |
PostAddContext
¶
Bases: TypedDict
Context passed to a backend's optional on_post_add() hook.
After add() completes local processing, portolan-cli checks
hasattr(backend, "on_post_add") and, if present, calls it with this
context. Backends that do not implement on_post_add() are silently
skipped.
Optional backend methods (checked via hasattr, NOT part of VersioningBackend): on_post_add(context: PostAddContext) -> None pull(remote_url: str, local_root: Path, collection: str, *, dry_run: bool) -> PullResult supports_push() -> bool push_blocked_message(remote: str | None) -> str
Note
The collection field is typed as Any to avoid a hard dependency on pystac.
At runtime, this will be a pystac.Collection instance.
PostAddItemContext
¶
Bases: TypedDict
Per-item context within PostAddContext.items list.
Attributes:
| Name | Type | Description |
|---|---|---|
item_id |
str
|
The STAC item identifier. |
item_dir |
Path
|
Path to the item directory containing assets. |
asset_files |
dict[str, tuple[Path, str, int]]
|
Dict mapping filename to (path, checksum, size) tuples. |
Version
dataclass
¶
A single version entry in the versions history.
Attributes:
| Name | Type | Description |
|---|---|---|
version |
str
|
Semantic version string (e.g., "1.0.0"). |
created |
datetime
|
UTC timestamp when this version was created. |
breaking |
bool
|
Whether this version has breaking changes. |
assets |
dict[str, Asset]
|
Mapping of filename to Asset metadata. |
changes |
list[str]
|
List of filenames that changed in this version. |
schema |
SchemaInfo | None
|
Optional schema fingerprint for breaking change detection. |
message |
str | None
|
Optional human-readable description of the change. |
Asset
dataclass
¶
A single asset (file) within a version.
Attributes:
| Name | Type | Description |
|---|---|---|
sha256 |
str
|
SHA-256 checksum of the file content. |
size_bytes |
int
|
File size in bytes. |
href |
str
|
Path to the asset, relative to catalog root
(e.g., "collection_id/item_id/filename.parquet").
push.py and pull.py resolve this via |
source_path |
str | None
|
Optional relative path to the original source file (e.g., the GeoJSON that was converted to this GeoParquet). |
source_mtime |
float | None
|
Optional Unix timestamp of the source file when conversion occurred. Used to detect when source has changed. |
mtime |
float | None
|
Optional Unix timestamp of the asset file itself. Used for fast-path change detection. |
feature_count |
int | None
|
Optional feature/row count (pixel count for rasters) captured when the asset was tracked. Lets a touched-but-identical asset read FRESH instead of a spurious STALE (heuristics). |
schema_fingerprint |
str | None
|
Optional hash of the asset schema captured when the asset was tracked. Used to detect breaking schema changes. |
SchemaInfo
dataclass
¶
Schema information for breaking change detection.
Attributes:
| Name | Type | Description |
|---|---|---|
type |
str
|
Schema type identifier (e.g., "geoparquet", "cog"). |
fingerprint |
dict[str, Any]
|
Type-specific schema fingerprint for change detection. |
parse_version(version_str)
¶
Parse a semantic version string into (major, minor, patch) tuple.
Handles standard semver, pre-release versions (CRITICAL #3), and build metadata. Returns (0, 0, 0) for invalid versions instead of raising.
Examples:
>>> parse_version("1.2.3")
(1, 2, 3)
>>> parse_version("1.0.0-beta") # Pre-release stripped
(1, 0, 0)
>>> parse_version("1.0.0+build.123") # Build metadata stripped
(1, 0, 0)
>>> parse_version("invalid")
(0, 0, 0)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version_str
|
str
|
Semantic version string (e.g., "1.2.3", "1.0.0-beta"). |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, int, int]
|
Tuple of (major, minor, patch) integers. Returns (0, 0, 0) if parsing fails. |
get_backend(name='file', catalog_root=None)
¶
Get a versioning backend by name.
Note: Creates a NEW instance on each call. This function does not implement singleton semantics; each call returns a fresh backend instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Backend name. "file" for built-in, "iceberg" for Iceberg (requires [iceberg] extra), or a name a plugin registers. |
'file'
|
catalog_root
|
Path | None
|
Catalog root the backend reads and writes. None lets the backend resolve the root itself. |
None
|
Returns:
| Type | Description |
|---|---|
VersioningBackend
|
VersioningBackend instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the backend is not found, its factory fails, or it does not implement VersioningBackend. A factory's own ValueError, such as the missing iceberg extra, passes through unchanged. |
Example
backend = get_backend() # Default file backend backend = get_backend("file") # Explicit file backend backend = get_backend("iceberg") # Iceberg backend (requires [iceberg] extra)
Catalogs¶
find_catalog_root(start_path=None, *, require_operational=True)
¶
Find the catalog root by walking up from the given path.
Searches for a managed Portolan catalog starting from start_path (or cwd if None) and walking up parent directories. This provides git-style behavior where commands work from any subdirectory within a catalog.
issue #290, this uses .portolan/config.yaml as the sole sentinel, unifying detection across all CLI commands. By default (require_operational=True), it also requires catalog.json to exist to avoid detecting half-initialized repos.
Security: Limited to MAX_CATALOG_SEARCH_DEPTH levels to prevent traversing to filesystem root where a malicious .portolan might exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_path
|
Path | None
|
Starting directory for search (defaults to cwd). |
None
|
require_operational
|
bool
|
If True (default), require .portolan/config.yaml AND catalog.json to exist. Set to False during init_catalog() when creating a new catalog where config.yaml is written before catalog.json. |
True
|
Returns:
| Type | Description |
|---|---|
Path | None
|
Path to catalog root if found, None otherwise. |
Examples:
>>> find_catalog_root(Path("/my-catalog/collection/item"))
PosixPath('/my-catalog')
>>> find_catalog_root(Path("/no-catalog-here"))
None
>>> find_catalog_root() # Uses current working directory
PosixPath('/my-catalog')
>>> # During init, check for config.yaml only (catalog.json not yet written)
>>> find_catalog_root(start_path, require_operational=False)
get_setting(key, cli_value=None, catalog_path=None, collection=None, collection_path=None)
¶
Resolve a setting with full precedence.
Precedence (highest to lowest):
1. CLI argument (cli_value)
2. Environment variable (PORTOLAN_
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Setting key (e.g., "remote", "aws_profile") |
required |
cli_value
|
Any | None
|
Value passed via CLI argument (highest precedence) |
None
|
catalog_path
|
Path | None
|
Path to catalog root for loading config file |
None
|
collection
|
str | None
|
Optional collection name for legacy collection-level config |
None
|
collection_path
|
Path | None
|
Optional path to collection directory for hierarchical lookup |
None
|
Returns:
| Type | Description |
|---|---|
Any | None
|
Resolved value, or None if not found at any level. |
write_json_atomic(path, data)
¶
Serialize data to path as JSON, atomically.
Writes to a temporary file in the destination's own directory and then
os.replaces it into position, so readers observe either the previous
content or the complete new content — never a truncated file. The temporary
file is removed if serialization or writing fails, and path is left
untouched in that case.
Output format (identical at every call site):
json.dumps(data, indent=2, ensure_ascii=False) plus a trailing newline,
encoded UTF-8. ensure_ascii=False keeps accented titles ("Córdoba")
literal instead of \\u00f3 escapes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Destination file. Parent directories are created if needed. |
required |
data
|
Any
|
Any JSON-serializable object. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
OSError
|
If the temporary file cannot be written or renamed. |
Extractors¶
Extractor
¶
Bases: Protocol
A source of data that Portolan can extract into a catalog.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The subcommand name, |
summary |
str
|
One line that describes the source. |
extract(source, output_dir, **options)
¶
Extract source into a catalog in output_dir.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Service URL or other source locator. |
required |
output_dir
|
Path
|
Directory of the catalog to write. |
required |
**options
|
Any
|
Source-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
ExtractionReport
|
The extraction report. |
cli_command()
¶
Return the Click command for portolan extract <name>.
Import the command module inside this method, so portolan --help
does not pay for it.
ExtractionReport
dataclass
¶
Complete extraction report for a service.
This is the top-level report that gets written to
.portolan/extraction-report.json.
Attributes:
| Name | Type | Description |
|---|---|---|
extraction_date |
str
|
ISO 8601 timestamp of extraction. |
source_url |
str
|
The service URL that was extracted. |
portolan_version |
str
|
Version of Portolan CLI used. |
gpio_version |
str
|
Version of geoparquet-io used. |
metadata_extracted |
MetadataExtracted
|
Harvested service metadata. |
layers |
list[LayerResult]
|
Results for each layer extraction attempt. |
summary |
ExtractionSummary
|
Aggregate statistics. |
folder_coverage |
FolderCoverage | None
|
Optional coverage data from recursive folder traversal. |
Layer sources¶
LayerSource
¶
Bases: Protocol
A data source that serves named layers.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The subcommand name, |
summary |
str
|
One line that describes the source. |
discover(source, options)
¶
Find the layers at source and the catalog-level metadata.
Do the work that happens once per run here, such as protocol
negotiation or a walk through a services root. Put what fetch
needs for a layer into LayerRef.data.
fetch(source, layer, dest)
¶
Write layer to dest as Parquet. Return the row count.
Raise EmptyLayerError for a layer with no features. Raise any
other exception for a failure. The driver retries both.
layer_metadata(source, layer)
¶
Return the metadata for the layer's collection, or None to skip it.
styles(source, layer, collection_dir)
¶
Write style or legend files next to the layer's data, or do nothing.
source_link(source, layer)
¶
Return the via link to the original layer, or None.
Discovery
dataclass
¶
What discover found at a source.
Attributes:
| Name | Type | Description |
|---|---|---|
source_url |
str
|
URL that the report records and that the other methods
receive as |
layers |
list[LayerRef]
|
Layers in discovery order. |
metadata |
MetadataExtracted | None
|
Service metadata for the report. None records the URL only. |
catalog_seed |
ExtractedMetadata | None
|
Metadata that seeds the catalog |
license_url |
str | None
|
License URL that the source publishes, if any. |
catalog_title |
str | None
|
Title for a new catalog, or None for the default. |
catalog_description |
str | None
|
Description for a new catalog, or None. |
root_metadata |
tuple[str | None, str | None] | None
|
|
folder_coverage |
FolderCoverage | None
|
Folders that a services-root traversal visited. |
LayerRef
dataclass
¶
One layer that a source serves.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
int
|
Layer id as the source knows it. The report records it. |
name |
str
|
Layer name. Filters and resume match on it. |
path |
str | None
|
Collection directory relative to the catalog, |
data |
Any
|
The source's own data for the layer, for |
notes |
tuple[str, ...]
|
Warnings that a dry run reports for the layer. |
LayerMetadata
dataclass
¶
Descriptive metadata for one layer's collection.
Attributes:
| Name | Type | Description |
|---|---|---|
title |
str | None
|
Collection title. |
description |
str | None
|
Collection description. |
keywords |
list[str] | None
|
Discovery keywords. |
layer_name |
str | None
|
Layer name for the processing notes. None uses the name. |
source_type |
str | None
|
|
source_url |
str | None
|
Layer URL for provenance. None uses the source link. |
record |
ExtractedMetadata | None
|
A full metadata record, such as an ISO 19139 record. It
replaces the fields above in |
record_label |
str
|
Name of the record kind for the progress message. |
SourceLink
dataclass
¶
A provenance via link from a collection to the original layer.
LayerSourceOptions
dataclass
¶
Options for run_layer_extraction.
Attributes:
| Name | Type | Description |
|---|---|---|
layer_filter |
list[str] | None
|
Glob patterns of layer names to include. None includes all. |
layer_exclude |
list[str] | None
|
Glob patterns of layer names to exclude. |
resume |
bool
|
Skip layers that succeeded or were empty in the previous report. |
dry_run |
bool
|
Report the layers without writing anything. |
raw |
bool
|
Write the layer files and the report, but no catalog. |
retries |
int
|
Attempts per layer. |
retry_delay |
float
|
Seconds before the first retry. Later retries double it. |
workers |
int
|
Layers to fetch at the same time. |
layer_timeout |
float | None
|
With more than one worker, seconds a layer may run before it is recorded as failed. None sets no limit. |
catalog_id |
str | None
|
Catalog id. None derives it from the output directory. |
license |
str | None
|
SPDX license identifier, or |
license_url |
str | None
|
URL of the license text. |
EmptyLayerError
¶
Bases: Exception
Raised by fetch when the layer has no features.
The driver records the layer as empty, not failed. A resumed run
does not try it again.
ExtractedMetadata
dataclass
¶
Source-agnostic metadata extracted from a data source.
This class holds metadata that can be extracted from various sources (ArcGIS services, GDAL files, etc.) and used to seed metadata.yaml.
All fields except source_type may be None if not available from the source. The seeding process will convert None values to TODO placeholders for required fields.
Attributes:
| Name | Type | Description |
|---|---|---|
source_type |
str
|
Identifier for the extraction source (e.g., "arcgis_featureserver"). |
source_url |
str | None
|
URL of the original data source. |
attribution |
str | None
|
Copyright or attribution text. |
description |
str | None
|
Human-readable description of the data. |
keywords |
list[str] | None
|
List of discovery keywords/tags. |
contact_name |
str | None
|
Name of the data maintainer/author. |
contact_email |
str | None
|
Email of the data maintainer (rarely available). |
license_raw |
str | None
|
Raw license text (not SPDX identifier). |
license_url |
str | None
|
URL of the license text, read out of license_raw. |
processing_notes |
str | None
|
Notes about data processing or updates. |
known_issues |
str | None
|
Known limitations or caveats. |
source_type
instance-attribute
¶
Identifier for the extraction source (e.g., 'arcgis_featureserver').
source_url = None
class-attribute
instance-attribute
¶
URL of the original data source.
attribution = None
class-attribute
instance-attribute
¶
Copyright or attribution text.
description = None
class-attribute
instance-attribute
¶
Human-readable description of the data.
keywords = None
class-attribute
instance-attribute
¶
List of discovery keywords/tags.
contact_name = None
class-attribute
instance-attribute
¶
Name of the data maintainer/author.
contact_email = None
class-attribute
instance-attribute
¶
Email of the data maintainer (rarely available from extraction).
license_url = None
class-attribute
instance-attribute
¶
Raw license text (not SPDX identifier, for human review).
processing_notes = None
class-attribute
instance-attribute
¶
Notes about data processing or update frequency.
known_issues = None
class-attribute
instance-attribute
¶
Known limitations, caveats, or access restrictions.
to_dict()
¶
Convert to a dictionary, excluding None values.
Returns:
| Type | Description |
|---|---|
dict[str, object]
|
Dictionary with non-None fields. |
run_layer_extraction(layer_source, source, output_dir, options=None, *, on_progress=None)
¶
Extract the layers of source into a Portolan catalog.
Steps: discover, filter, check the license, resume, fetch with retry,
save the report to .portolan/extraction-report.json, then build the
catalog unless options.raw is set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_source
|
LayerSource
|
The source implementation. |
required |
source
|
str
|
Service URL or other source locator. |
required |
output_dir
|
Path
|
Catalog directory. |
required |
options
|
LayerSourceOptions | None
|
Extraction options. None uses the defaults. |
None
|
on_progress
|
Callable[[ExtractionProgress], None] | None
|
Called with progress for each layer. |
None
|
Returns:
| Type | Description |
|---|---|
ExtractionReport
|
The extraction report. |
Raises:
| Type | Description |
|---|---|
MissingLicenseError
|
Before any download, when no license can be
resolved and |
InputValidationError
|
Before any write, when a layer's |
extractor_from(layer_source)
¶
Wrap a LayerSource as an Extractor for portolan_extractors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_source
|
LayerSource
|
The source implementation. |
required |
Returns:
| Type | Description |
|---|---|
LayerSourceExtractor
|
An extractor with the source's name and summary. |
Remote storage¶
upload_file(source, destination, *, profile=None, dry_run=False, s3_endpoint=None, s3_region=None, s3_use_ssl=None, chunk_concurrency=4)
¶
Upload a single file to S3/GCS/Azure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Path
|
Local file path |
required |
destination
|
str
|
Object store URL (e.g., s3://bucket/key) |
required |
profile
|
str | None
|
AWS profile name (for S3 only) |
None
|
dry_run
|
bool
|
If True, show what would be uploaded without actually uploading |
False
|
s3_endpoint
|
str | None
|
Custom S3-compatible endpoint (e.g., "minio.example.com:9000") |
None
|
s3_region
|
str | None
|
S3 region (default: auto-detected) |
None
|
s3_use_ssl
|
bool | None
|
Whether to use HTTPS for S3 endpoint (default: True) |
None
|
chunk_concurrency
|
int
|
Max concurrent chunks per file (default: 4) |
4
|
Returns:
| Type | Description |
|---|---|
UploadResult
|
UploadResult with upload statistics |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If source file does not exist |
Example
result = upload_file(Path("data.parquet"), "s3://bucket/data.parquet") if result.success: ... print(f"Uploaded {result.files_uploaded} file(s)")
download_file(source, destination, *, profile=None, dry_run=False, overwrite=True, s3_endpoint=None, s3_region=None, s3_use_ssl=None, chunk_concurrency=4)
¶
Download a single file from S3/GCS/Azure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Object store URL (e.g., s3://bucket/key) |
required |
destination
|
Path
|
Local file path or directory |
required |
profile
|
str | None
|
AWS profile name (for S3 only) |
None
|
dry_run
|
bool
|
If True, show what would be downloaded without actually downloading |
False
|
overwrite
|
bool
|
If False, skip existing files (default: True) |
True
|
s3_endpoint
|
str | None
|
Custom S3-compatible endpoint (e.g., "minio.example.com:9000") |
None
|
s3_region
|
str | None
|
S3 region (default: auto-detected) |
None
|
s3_use_ssl
|
bool | None
|
Whether to use HTTPS for S3 endpoint (default: True) |
None
|
chunk_concurrency
|
int
|
Max concurrent chunks per file (default: 4) |
4
|
Returns:
| Type | Description |
|---|---|
DownloadResult
|
DownloadResult with download statistics |
Raises:
| Type | Description |
|---|---|
ValueError
|
If source or destination is empty/invalid |
Example
result = download_file("s3://bucket/data.parquet", Path("data.parquet")) if result.success: ... print(f"Downloaded {result.files_downloaded} file(s)")
PullResult
dataclass
¶
Result of a pull operation.
Attributes:
| Name | Type | Description |
|---|---|---|
success |
bool
|
True if pull completed successfully. |
files_downloaded |
int
|
Number of files downloaded. |
files_skipped |
int
|
Number of files skipped (already up to date). |
local_version |
str | None
|
Local version before pull. |
remote_version |
str | None
|
Remote version (None in dry-run mode since no network check). |
uncommitted_changes |
list[str]
|
List of files with uncommitted changes that blocked pull. |
up_to_date |
bool
|
True if already at remote version (nothing to pull). |
dry_run |
bool
|
True if this was a dry-run operation (no network calls made). |
files_restored |
int
|
Number of files restored (re-downloaded because missing locally). |
Terminal output¶
success(message, *, file=None, nl=True, dry_run=False)
¶
Print a success message with green checkmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The message to display. |
required |
file
|
TextIO | None
|
File to write to (default: stdout). |
None
|
nl
|
bool
|
Whether to print a newline after the message. |
True
|
dry_run
|
bool
|
If True, prefix with [DRY RUN] to indicate simulation mode. |
False
|
Example
success("Wrote output.parquet (1.2 MB)") ✓ Wrote output.parquet (1.2 MB)
success("Would write file", dry_run=True) ✓ [DRY RUN] Would write file
info(message, *, file=None, nl=True, dry_run=False)
¶
Print an info message with blue arrow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The message to display. |
required |
file
|
TextIO | None
|
File to write to (default: stdout). |
None
|
nl
|
bool
|
Whether to print a newline after the message. |
True
|
dry_run
|
bool
|
If True, prefix with [DRY RUN] to indicate simulation mode. |
False
|
Example
info("Reading data.shp (4,231 features)") → Reading data.shp (4,231 features)
info("Would read file", dry_run=True) → [DRY RUN] Would read file
warn(message, *, file=None, nl=True, dry_run=False)
¶
Print a warning message with yellow warning symbol.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The message to display. |
required |
file
|
TextIO | None
|
File to write to (default: stderr). |
None
|
nl
|
bool
|
Whether to print a newline after the message. |
True
|
dry_run
|
bool
|
If True, prefix with [DRY RUN] to indicate simulation mode. |
False
|
Example
warn("Missing thumbnail (recommended)") âš Missing thumbnail (recommended)
warn("Would skip validation", dry_run=True) âš [DRY RUN] Would skip validation
error(message, *, file=None, nl=True, dry_run=False)
¶
Print an error message with red X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The message to display. |
required |
file
|
TextIO | None
|
File to write to (default: stderr). |
None
|
nl
|
bool
|
Whether to print a newline after the message. |
True
|
dry_run
|
bool
|
If True, prefix with [DRY RUN] to indicate simulation mode. |
False
|
Example
error("No geometry column (required)") ✗ No geometry column (required)
error("Would fail validation", dry_run=True) ✗ [DRY RUN] Would fail validation
detail(message, *, file=None, nl=True, dry_run=False)
¶
Print a detail/progress message in dimmed text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The message to display. |
required |
file
|
TextIO | None
|
File to write to (default: stdout). |
None
|
nl
|
bool
|
Whether to print a newline after the message. |
True
|
dry_run
|
bool
|
If True, prefix with [DRY RUN] to indicate simulation mode. |
False
|
Example
detail("Processing chunk 3/10...") Processing chunk 3/10...
detail("Would process chunk", dry_run=True) [DRY RUN] Would process chunk
JSON output for commands¶
success_envelope(command, data)
¶
Create a success envelope with the given command and data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
command
|
str
|
Name of the command (e.g., "scan", "init", "check") |
required |
data
|
dict[str, Any]
|
Command-specific payload |
required |
Returns:
| Type | Description |
|---|---|
OutputEnvelope
|
OutputEnvelope with success=True and errors=None |
error_envelope(command, errors, *, data=None)
¶
Create an error envelope with the given command and errors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
command
|
str
|
Name of the command (e.g., "scan", "init", "check") |
required |
errors
|
list[ErrorDetail]
|
List of ErrorDetail objects describing the errors |
required |
data
|
dict[str, Any] | None
|
Optional partial data to include (default: empty dict) |
None
|
Returns:
| Type | Description |
|---|---|
OutputEnvelope
|
OutputEnvelope with success=False and the provided errors |
ErrorDetail
dataclass
¶
Structure for individual error entries in the errors array.
Attributes:
| Name | Type | Description |
|---|---|---|
type |
str
|
Error class name (e.g., "CatalogExistsError", "FileNotFoundError") |
message |
str
|
Human-readable error description |
code |
str | None
|
Optional structured error code (e.g., "PRTLN-CAT001") |
to_dict()
¶
Convert to dictionary for JSON serialization.
Warnings¶
PluginWarning
¶
Bases: UserWarning
A plugin misbehaved. The CLI keeps running without its contribution.
Format handler helpers¶
FileInfo
dataclass
¶
Metadata extracted from a geospatial file.
Combines format-specific metadata from upstream libraries with version tracking from versions.json.
Attributes:
| Name | Type | Description |
|---|---|---|
path |
Path
|
Path to the file. |
format |
str
|
File format (GeoParquet, COG, etc.). |
crs |
str | None
|
Coordinate reference system (EPSG code or WKT). |
bbox |
list[float] | None
|
Bounding box as [minx, miny, maxx, maxy]. |
feature_count |
int | None
|
Number of features (vector files only). |
width |
int | None
|
Image width in pixels (raster files only). |
height |
int | None
|
Image height in pixels (raster files only). |
band_count |
int | None
|
Number of bands (raster files only). |
dtype |
str | None
|
Data type (raster files only). |
version |
str | None
|
Version string from versions.json (if tracked). |
geometry_type |
str | None
|
Geometry type (vector files only). |
InvalidOutputError
¶
Bases: ConversionError
Raised when a format handler writes a file that fails its own validation.
Error code: PRTLN-CNV008
RASTER_EXTENSIONS = _reg.extensions_where(routes_as='raster')
module-attribute
¶
VECTOR_EXTENSIONS = _reg.extensions_where(routes_as='vector')
module-attribute
¶
aggregate_table_extension(collection, metadata, context)
¶
Aggregate table:* metadata from tracked parquet on disk (issues #304/#447).
Runs only when the batch holds at least one NEW GeoParquet file.
Recomputes aggregates from ALL tracked parquet files on disk instead of
carrying forward stale counts. This prevents double-counting on re-add and
stale counts when files are replaced. The built-in GeoParquet handler
calls this from describe_collection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
Collection
|
The collection in memory. |
required |
metadata
|
list[Any]
|
What the handler read from each new file. |
required |
context
|
FormatContext
|
Where the collection sits, and the merge strategy. |
required |
convert_raster(source, dest_dir, catalog_root=None, *, reoptimize=False)
¶
Convert a raster to a COG. add and check --fix both use this (#947).
A source that is already a COG is kept, unless reoptimize asks to
rewrite it with the current settings (issue #530). Otherwise
convert.write_cog writes the COG with the catalog's conversion.cog
settings: DEFLATE, 512x512 tiles, and a predictor and overview resampling
derived from the source dtype by default (issue #690).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Path
|
Source raster file. |
required |
dest_dir
|
Path
|
Destination directory. |
required |
catalog_root
|
Path | None
|
Catalog root, for the COG settings. None uses the defaults. |
None
|
reoptimize
|
bool
|
Rewrite a source that is already a COG. |
False
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the output COG file. |
convert_vector(source, dest_dir, catalog_root=None, *, reconvert=False, settings=None, partition_dir=None)
¶
Convert a vector file to GeoParquet. add and check --fix both use this (#947).
Applies the catalog's conversion.vector settings, which sort rows and
add a bbox covering column by default (issue #805). A .parquet source
is copied unless :func:_needs_spatial_rewrite says it must be rewritten.
add writes one file. A configured spatial index becomes a column of
that file, whatever partition says. check --fix passes
partition_dir instead, and the table is written as Hive partitions by
that index. Each source gets its own partition_dir, because two
sources in one directory share cells, and geoparquet-io does not write over
a cell that exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Path
|
Source vector file. |
required |
dest_dir
|
Path
|
Destination directory. |
required |
catalog_root
|
Path | None
|
Catalog root, for reading |
None
|
reconvert
|
bool
|
True when the operator asked to re-convert from source, which rewrites a Parquet file whatever its footer already declares. |
False
|
settings
|
VectorSettings | None
|
Explicit vector settings. None reads them from the catalog. |
None
|
partition_dir
|
Path | None
|
Directory for Hive partitions by the configured spatial
index. It applies only to a non-Parquet source, when the settings
set |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the output GeoParquet file, or |
Path
|
table was written as partitions. |
describe_raster_item(item, output, metadata, context)
¶
Add the raster fields to a new item: render, band statistics, nodata, extension.
The built-in COG handler calls this from describe_item (#947).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
Item
|
The new item. |
required |
output
|
Path
|
The COG. |
required |
metadata
|
Any
|
What |
required |
context
|
FormatContext
|
Where the file sits. |
required |
describe_vector_item(item, output, metadata, context)
¶
Add the vector fields to a new item: column statistics and the vector extension.
The built-in GeoParquet handler calls this from describe_item (#947).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
Item
|
The new item. |
required |
output
|
Path
|
The GeoParquet file. |
required |
metadata
|
Any
|
What |
required |
context
|
FormatContext
|
Where the file sits. |
required |
extract_cog_metadata(path)
¶
Extract metadata from a COG file.
Uses rasterio to read file metadata without loading pixel data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to COG file. |
required |
Returns:
| Type | Description |
|---|---|
COGMetadata
|
COGMetadata with extracted information. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If file doesn't exist. |
RasterioIOError
|
If file is not valid raster. |
extract_flatgeobuf_metadata(path)
¶
Extract metadata from a FlatGeobuf file.
Uses pyogrio to read header metadata without loading all data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to FlatGeobuf file. |
required |
Returns:
| Type | Description |
|---|---|
FlatGeobufMetadata
|
FlatGeobufMetadata with extracted information. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If file doesn't exist. |
ValueError
|
If file is not a valid FlatGeobuf file. |
extract_geoparquet_metadata(path)
¶
Extract metadata from a GeoParquet file.
Uses pyarrow to read file metadata without loading all data. Parses GeoParquet geo metadata from file's custom metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to GeoParquet file. |
required |
Returns:
| Type | Description |
|---|---|
GeoParquetMetadata
|
GeoParquetMetadata with extracted information. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If file doesn't exist. |
Note
For Parquet files without GeoParquet metadata, returns metadata with None/empty fields for geo-specific properties (bbox, crs, geometry_type).
extract_pmtiles_metadata(path)
¶
Extract metadata from a PMTiles file.
Uses the pmtiles package to read header metadata without loading tiles.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to PMTiles file. |
required |
Returns:
| Type | Description |
|---|---|
PMTilesMetadata
|
PMTilesMetadata with extracted information. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If file doesn't exist. |
ValueError
|
If file is not a valid PMTiles file. |
ImportError
|
If pmtiles package is not installed. |
inspect_cog(path, *, catalog_root=None)
¶
Extract metadata from a COG file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the COG file. |
required |
catalog_root
|
Path | None
|
Optional catalog root for version lookup. |
None
|
Returns:
| Type | Description |
|---|---|
FileInfo
|
FileInfo with COG metadata. |
inspect_geoparquet(path, *, catalog_root=None)
¶
Extract metadata from a GeoParquet file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the GeoParquet file. |
required |
catalog_root
|
Path | None
|
Optional catalog root for version lookup. |
None
|
Returns:
| Type | Description |
|---|---|
FileInfo
|
FileInfo with GeoParquet metadata. |
is_geojson(path)
¶
Check if a .json file is GeoJSON.
Uses prefix reading (first 8KB) to avoid OOM on large files. Searches for GeoJSON type tokens without full JSON parsing.
STAC items/collections/catalogs are NOT GeoJSON even though they have "type": "Feature". They are identified by "stac_version".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to JSON file. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the prefix looks like GeoJSON. |
is_geoparquet(path)
¶
Check if a Parquet file has GeoParquet metadata.
GeoParquet files have a 'geo' key in their schema metadata that contains geospatial column information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the Parquet file. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool
|
True if the file is a valid GeoParquet, False otherwise. |
|
Note |
bool
|
Returns False for both non-geo Parquet AND invalid/corrupted files. |
bool
|
Use is_valid_parquet() first if you need to distinguish these cases. |
partition_vector_file(path, catalog_root)
¶
Partition a converted GeoParquet file when partition_strategy says so.
The partitions go next to the file in Hive directories, through
partition_geoparquet. The file is removed only when the step wrote at
least one partition file.
The directory must hold no partitions for the strategy. geoparquet-io does not write over a cell that exists, so a second source, or a re-add, would lose the rows in each shared cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The converted GeoParquet file. |
required |
catalog_root
|
Path | None
|
Catalog root, for the settings. |
required |
Returns:
| Type | Description |
|---|---|
list[Path]
|
The partition files, or an empty list when the file stays whole. |
Raises:
| Type | Description |
|---|---|
PartitionCollisionError
|
The directory already holds partitions. |
validate_cog(path)
¶
Validate that the output file is a valid COG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the TIFF file to validate. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Error message if validation failed, None if valid. |
validate_geoparquet(path)
¶
Validate that the output is valid GeoParquet (file or partitioned directory).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the parquet file or hive-partitioned directory. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Error message if validation failed, None if valid. |
write_raster_thumbnail(cog_path, catalog_root)
¶
Write <stem>.thumb.jpg next to a COG when the COG settings ask for it.
A thumbnail that exists stays, so a re-add keeps a hand-made thumbnail.
Portolan cannot tell a hand-made thumbnail from one it wrote, so an old
thumbnail also stays after the imagery changes. Delete <stem>.thumb.jpg
to make the next conversion write a new one. A thumbnail is optional, so a
failure only warns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cog_path
|
Path
|
The COG. |
required |
catalog_root
|
Path | None
|
Catalog root, for the COG settings. |
required |
Plugin hooks¶
Use @hookimpl to implement these hooks from portolan_cli.hookspecs.
portolan_backends()
¶
Return versioning backends by name.
Each value is called as factory(catalog_root=path) and must return a
VersioningBackend. A class satisfies this. Import heavy dependencies
inside the factory, so a catalog that does not use the backend does not
pay for them. A catalog selects a backend with the backend setting.
Returns:
| Type | Description |
|---|---|
dict[str, BackendFactory]
|
Mapping of backend name to factory. |
portolan_formats()
¶
Return format handlers.
A handler converts a source format to a cloud-native file and reads its
metadata. See portolan_cli.api.FormatHandler. For one file, a
conversion.handlers pin wins, then a plugin handler, then a built-in
handler.
Returns:
| Type | Description |
|---|---|
list[FormatHandler]
|
Format handler objects. |
portolan_enrich_item(item, context)
¶
Add STAC fields to an item before Portolan publishes it.
The hook receives the item JSON as a dict and changes it in place. It may
add properties, stac_extensions entries, and asset fields. Portolan
restores id, type, stac_version, and links after the hook.
The hook runs every time portolan add writes the item. It must give
the same result when it runs twice.
Portolan calls each implementation on its own. A hook wrapper
(wrapper=True or hookwrapper=True) is skipped with a warning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
dict[str, Any]
|
STAC item JSON. |
required |
context
|
EnrichContext
|
Where the item lives. |
required |
portolan_enrich_collection(collection, context)
¶
Add STAC fields to a collection before Portolan publishes it.
The rules of portolan_enrich_item apply.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
dict[str, Any]
|
STAC collection JSON. |
required |
context
|
EnrichContext
|
Where the collection lives. |
required |
portolan_extractors()
¶
Return data sources for portolan extract.
Each extractor becomes portolan extract <name>. portolan extract
--help imports the hook module, so keep it light. The extractor imports
its Click command inside cli_command().
A layer-based service can implement LayerSource and wrap it with
portolan_cli.api.extractor_from. It then gets filters, resume,
retry, the report, and catalog setup from Portolan.
Returns:
| Type | Description |
|---|---|
list[Extractor]
|
Extractors. A name that a built-in extractor uses is ignored. |
Plugin testing¶
Call these checks from portolan_cli.testing in your plugin tests.
ConformanceError
¶
Bases: AssertionError
A plugin object does not meet its protocol.
check_backend(factory, catalog_root)
¶
Check a versioning backend factory against VersioningBackend.
The check creates the backend in catalog_root, publishes two versions
of one collection, and reads them back. rollback and prune may
raise NotImplementedError, as the built-in file backend does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factory
|
Callable[..., Any]
|
What |
required |
catalog_root
|
Path
|
An empty directory the backend may write to. |
required |
Raises:
| Type | Description |
|---|---|
ConformanceError
|
With one line per problem found. |
check_extractor(extractor, source=None, output_dir=None, **options)
¶
Check an extractor against Extractor.
The check reads name and summary and builds the Click command.
With source and output_dir, it also runs extract() and checks
the report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extractor
|
Any
|
What |
required |
source
|
str | None
|
A source to extract, or None to skip the run. |
None
|
output_dir
|
Path | None
|
An empty directory for the run. |
None
|
**options
|
Any
|
Options for |
{}
|
Raises:
| Type | Description |
|---|---|
ConformanceError
|
With one line per problem found. |
check_format_handler(handler, sample, output_dir)
¶
Check a format handler against FormatHandler with a sample file.
The check reads the handler attributes, converts sample into
output_dir, and reads the metadata of the result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Any
|
What |
required |
sample
|
Path
|
A small source file in a format the handler claims. |
required |
output_dir
|
Path
|
An empty directory the handler may write to. |
required |
Raises:
| Type | Description |
|---|---|
ConformanceError
|
With one line per problem found. |