Skip to content

Commit

Permalink
feat(python): add migrate
Browse files Browse the repository at this point in the history
  • Loading branch information
gadomski committed Aug 29, 2024
1 parent 2b34ae1 commit 1a70402
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 8 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install maturin and pytest
run: pip install maturin pytest
- name: Install dev requirements
run: pip install -r python/requirements-dev.txt
- name: Build
run: maturin build --manifest-path python/Cargo.toml --out dist
- name: Install stacrs
run: pip install stacrs --find-links dist --no-index
- name: Check
run: ruff check python && ruff format --check python && mypy python
- name: Test
run: pytest python/tests
check:
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ This monorepo contains several crates:

## Bindings

This repo includes Python bindings, called **stacrs**.
### Python

**stacrs** is a small, no-dependency Python library that uses **stac-rs** under the hood.
Install with **pip**:

```shell
pip install stacrs
```

See [the README](./python/README.md) for more information.
See [the documentation](https://stacrs.readthedocs.io/) for more information.

## Development

Expand Down
2 changes: 2 additions & 0 deletions python/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@ docs/_build/

# Pyenv
.python-version

site
13 changes: 13 additions & 0 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `migrate` ([#309](https://github.com/stac-utils/stac-rs/pull/309))
- `validate` and docs ([#307](https://github.com/stac-utils/stac-rs/pull/307))

## [0.0.2] - 2024-08-28

Non-functional release to fix releasing from Github actions.

## [0.0.1] - 2024-08-28

Initial release.

[Unreleased]: https://github.com/stac-utils/stac-rs/compare/python-v0.0.2...main
[0.0.2]: https://github.com/stac-utils/stac-rs/compare/python-v0.0.1...python-v0.0.2
[0.0.1]: https://github.com/stac-utils/stac-rs/releases/tag/python-v0.0.1
3 changes: 3 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/stac-utils/stac-rs/ci.yml?branch=main&style=for-the-badge)](https://github.com/stac-utils/stac-rs/actions/workflows/ci.yml)
[![PyPI - Version](https://img.shields.io/pypi/v/stacrs?style=for-the-badge)](https://pypi.org/project/stacrs)
[![Read the Docs](https://img.shields.io/readthedocs/stacrs?style=for-the-badge)](https://stacrs.readthedocs.io/)
![PyPI - License](https://img.shields.io/pypi/l/stacrs?style=for-the-badge)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg?style=for-the-badge)](./CODE_OF_CONDUCT)

Expand All @@ -23,6 +24,8 @@ import stacrs
stacrs.validate_href("https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/simple-item.json")
```

See [the documentation](https://stacrs.readthedocs.io/) for more information.

## Other info

This crate is part of the [stac-rs](https://github.com/stac-utils/stac-rs) monorepo, see its README for contributing and license information.
6 changes: 5 additions & 1 deletion python/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

API documentation for **stacrs**.

## Validation
## Migrate

::: stacrs.migrate

## Validate

::: stacrs.validate
::: stacrs.validate_href
2 changes: 1 addition & 1 deletion python/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ plugins:
show_root_heading: true
show_signature: true
show_signature_annotations: true
separate_signature: false
separate_signature: true
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
Expand Down
1 change: 1 addition & 0 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dynamic = ["version"]

[project.urls]
Repository = "https://github.com/stac-utils/stac-rs/tree/main/python"
Documentation = "https://stacrs.readthedocs.io/"
Issues = "https://github.com/stac-utils/stac-rs/issues"

[tool.maturin]
Expand Down
4 changes: 4 additions & 0 deletions python/requirements-dev.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
maturin
mypy
ruff
pytest
20 changes: 20 additions & 0 deletions python/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This file was autogenerated by uv via the following command:
# uv pip compile python/requirements-dev.in
iniconfig==2.0.0
# via pytest
maturin==1.7.1
# via -r python/requirements-dev.in
mypy==1.11.2
# via -r python/requirements-dev.in
mypy-extensions==1.0.0
# via mypy
packaging==24.1
# via pytest
pluggy==1.5.0
# via pytest
pytest==8.3.2
# via -r python/requirements-dev.in
ruff==0.6.2
# via -r python/requirements-dev.in
typing-extensions==4.12.2
# via mypy
41 changes: 40 additions & 1 deletion python/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
use pyo3::{create_exception, exceptions::PyException, prelude::*, types::PyDict};
use stac::Value;
use stac::{Migrate, Value};
use stac_validate::Validate;

create_exception!(stacrs, StacrsError, PyException, "An error in stacrs");

/// Migrates a STAC dictionary to another version.
///
/// Migration can be as simple as updating the `stac_version` attribute, but
/// sometimes can be more complicated. For example, when migrating to v1.1.0,
/// [eo:bands and raster:bands should be consolidated to the new bands
/// structure](https://github.com/radiantearth/stac-spec/releases/tag/v1.1.0-beta.1).
///
/// See [the stac-rs
/// documentation](https://docs.rs/stac/latest/stac/enum.Version.html) for
/// supported versions.
///
/// Args:
/// value (dict[str, Any]): The STAC value to migrate
/// version (str | None): The version to migrate to. If not provided, the
/// value will be migrated to the latest stable version.
///
/// Examples:
/// >>> with open("examples/simple-item.json") as f:
/// >>> item = json.load(f)
/// >>> item = stacrs.migrate(item, "1.1.0-beta.1")
/// >>> assert item["stac_version"] == "1.1.0-beta.1"
#[pyfunction]
#[pyo3(signature = (value, version=None))]
fn migrate<'py>(value: &Bound<'py, PyDict>, version: Option<&str>) -> PyResult<Bound<'py, PyDict>> {
let py = value.py();
let value: Value = pythonize::depythonize(value)?;
let version = version
.map(|version| version.parse())
.transpose()
.map_err(|err: stac::Error| StacrsError::new_err(err.to_string()))?
.unwrap_or_default();
let value = value
.migrate(version)
.map_err(|err| StacrsError::new_err(err.to_string()))?;
let value = pythonize::pythonize(py, &value)?;
value.downcast_into().map_err(PyErr::from)
}

/// Validates a single href with json-schema.
///
/// Args:
Expand Down Expand Up @@ -64,6 +102,7 @@ fn validate_value(value: Value) -> PyResult<()> {
/// A collection of functions for working with STAC, using Rust under the hood.
#[pymodule]
fn stacrs(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(migrate, m)?)?;
m.add_function(wrap_pyfunction!(validate_href, m)?)?;
m.add_function(wrap_pyfunction!(validate, m)?)?;
m.add("StacrsError", m.py().get_type_bound::<StacrsError>())?;
Expand Down
3 changes: 2 additions & 1 deletion python/stacrs.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any
from typing import Any, Optional

class StacrsError(Exception): ...

def migrate(value: dict[str, Any], version: Optional[str] = None) -> dict[str, Any]: ...
def validate_href(href: str) -> None: ...
def validate(value: dict[str, Any]) -> None: ...
8 changes: 8 additions & 0 deletions python/tests/test_migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import Any

import stacrs


def test_migrate(item: dict[str, Any]) -> None:
item = stacrs.migrate(item, version="1.1.0-beta.1")
assert item["stac_version"] == "1.1.0-beta.1"

0 comments on commit 1a70402

Please sign in to comment.