Skip to content

release: v1.13.0 #1550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

repos:
- repo: https://github.com/asottile/pyupgrade
rev: v3.18.0
rev: v3.19.1
hooks:
- id: pyupgrade
args:
Expand Down
11 changes: 7 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

## [v1.13.0] - 2025-04-15

### Added

- Projection extension: migrate Assets and Item-Assets ([#1549](https://github.com/stac-utils/pystac/pull/1549))
Expand All @@ -13,13 +15,13 @@
- `proj:epsg` migration when `None` ([#1544](https://github.com/stac-utils/pystac/pull/1544))
- fixed missing parameter "title" in pystac.extensions.classification.Classification ([#1539](https://github.com/stac-utils/pystac/pull/1539))

## [v1.12.2]
## [v1.12.2] - 2025-02-19

### Fixed

- Make sure that `VersionRange` has `VersionID`s rather than strings ([#1512](https://github.com/stac-utils/pystac/pull/1512))

## [v1.12.1]
## [v1.12.1] - 2025-01-27

### Changed

Expand All @@ -29,7 +31,7 @@

- Fall back to `epsg` when `code` is not present in the Projection extension ([#1505](https://github.com/stac-utils/pystac/pull/1505), [#1510](https://github.com/stac-utils/pystac/pull/1510))

## [v1.12.0]
## [v1.12.0] - 2025-01-23

### Added

Expand Down Expand Up @@ -921,7 +923,8 @@ use `Band.create`

Initial release.

[Unreleased]: <https://github.com/stac-utils/pystac/compare/v1.12.2..main>
[Unreleased]: <https://github.com/stac-utils/pystac/compare/v1.13.0..main>
[v1.13.0]: <https://github.com/stac-utils/pystac/compare/v1.12.2..v1.13.0>
[v1.12.2]: <https://github.com/stac-utils/pystac/compare/v1.12.1..v1.12.2>
[v1.12.1]: <https://github.com/stac-utils/pystac/compare/v1.12.0..v1.12.1>
[v1.12.0]: <https://github.com/stac-utils/pystac/compare/v1.11.0..v1.12.0>
Expand Down
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,19 @@

extlinks = {
"tutorial": (
"https://github.com/stac-utils/pystac/" "tree/{}/docs/tutorials/%s".format(
"https://github.com/stac-utils/pystac/tree/{}/docs/tutorials/%s".format(
git_branch
),
"%s tutorial",
),
"stac-spec": (
"https://github.com/radiantearth/stac-spec/tree/" "v{}/%s".format(
"https://github.com/radiantearth/stac-spec/tree/v{}/%s".format(
STACVersion.DEFAULT_STAC_VERSION
),
"%s path",
),
"stac-api-spec": (
"https://github.com/radiantearth/stac-api-spec/tree/" "v{}/%s".format(
"https://github.com/radiantearth/stac-api-spec/tree/v{}/%s".format(
STACVersion.DEFAULT_STAC_API_VERSION
),
"%s path",
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/adding-new-and-custom-extensions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
" self.properties[\"timestamp\"] = datetime_to_str(v)\n",
"\n",
" def __repr__(self) -> str:\n",
" return \"<OrderEvent \" f\"type={self.event_type} \" f\"timestamp={self.timestamp}>\"\n",
" return f\"<OrderEvent type={self.event_type} timestamp={self.timestamp}>\"\n",
"\n",
" def apply(\n",
" self,\n",
Expand Down
3 changes: 1 addition & 2 deletions docs/tutorials/creating-a-landsat-stac.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1418,8 +1418,7 @@
" offset=11,\n",
" length=1,\n",
" description=(\n",
" \"Terrain not visible from sensor due to \"\n",
" \"intervening terrain\"\n",
" \"Terrain not visible from sensor due to intervening terrain\"\n",
" ),\n",
" classes=[\n",
" Classification.create(\n",
Expand Down
12 changes: 6 additions & 6 deletions pystac/extensions/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@ def color_hint(self) -> str | None:
def color_hint(self, v: str | None) -> None:
if v is not None:
match = COLOR_HINT_PATTERN.match(v)
assert (
v is None or match is not None and match.group() == v
), "Must format color hints as '^([0-9A-F]{6})$'"
assert v is None or match is not None and match.group() == v, (
"Must format color hints as '^([0-9A-F]{6})$'"
)
self.properties["color_hint"] = v
else:
self.properties.pop("color_hint", None)
Expand Down Expand Up @@ -343,9 +343,9 @@ def apply(
assert offset >= 0, "Non-negative offsets only"
assert length >= 1, "Positive field lengths only"
assert len(classes) > 0, "Must specify at least one class"
assert (
roles is None or len(roles) > 0
), "When set, roles must contain at least one item"
assert roles is None or len(roles) > 0, (
"When set, roles must contain at least one item"
)

@classmethod
def create(
Expand Down
2 changes: 1 addition & 1 deletion pystac/extensions/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def validated_code(v: str) -> str:
raise ValueError("Invalid Grid code: must be str")
if not CODE_PATTERN.fullmatch(v):
raise ValueError(
f"Invalid Grid code: {v}" f" does not match the regex {CODE_REGEX}"
f"Invalid Grid code: {v} does not match the regex {CODE_REGEX}"
)
return v

Expand Down
2 changes: 1 addition & 1 deletion pystac/serialization/common_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def merge_common_properties(
collection_props = collection["properties"]
else:
raise ValueError(
"{} is expected to be a Collection or " "dict but is neither.".format(
"{} is expected to be a Collection or dict but is neither.".format(
collection
)
)
Expand Down
34 changes: 16 additions & 18 deletions pystac/serialization/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,29 @@ def _migrate_datetime_range(
return None


def _get_object_migrations() -> (
dict[str, Callable[[dict[str, Any], STACVersionID, STACJSONDescription], None]]
):
def _get_object_migrations() -> dict[
str, Callable[[dict[str, Any], STACVersionID, STACJSONDescription], None]
]:
return {
pystac.STACObjectType.CATALOG: _migrate_catalog,
pystac.STACObjectType.COLLECTION: _migrate_collection,
pystac.STACObjectType.ITEM: _migrate_item,
}


def _get_removed_extension_migrations() -> (
dict[
str,
tuple[
list[STACObjectType] | None,
None
| (
Callable[
[dict[str, Any], STACVersionID, STACJSONDescription],
set[str] | None,
]
),
],
]
):
def _get_removed_extension_migrations() -> dict[
str,
tuple[
list[STACObjectType] | None,
None
| (
Callable[
[dict[str, Any], STACVersionID, STACJSONDescription],
set[str] | None,
]
),
],
]:
"""Handles removed extensions.

This does not handle renamed extension or extensions that were absorbed
Expand Down
12 changes: 6 additions & 6 deletions pystac/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,12 +464,12 @@ def extract_coords(coords: list[list[float] | list[list[Any]]]) -> None:
for x in coords:
# This handles points
if isinstance(x, float):
assert isinstance(
coords[0], float
), f"Type mismatch: {coords[0]} is not a float"
assert isinstance(
coords[1], float
), f"Type mismatch: {coords[1]} is not a float"
assert isinstance(coords[0], float), (
f"Type mismatch: {coords[0]} is not a float"
)
assert isinstance(coords[1], float), (
f"Type mismatch: {coords[1]} is not a float"
)
lats.append(coords[0])
lons.append(coords[1])
return
Expand Down
2 changes: 1 addition & 1 deletion pystac/validation/local_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __init__(self) -> None:

def registry(self) -> Any:
return Registry().with_resources(
[(k, Resource.from_contents(v)) for k, v in self.schema_cache.items()] # type: ignore
[(k, Resource.from_contents(v)) for k, v in self.schema_cache.items()]
)

def _validate_from_local(
Expand Down
2 changes: 1 addition & 1 deletion pystac/validation/stac_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def retrieve(schema_uri: str) -> Resource[dict[str, Any]]:
return Resource.from_contents(self._get_schema(schema_uri))

return Registry(retrieve=retrieve).with_resources( # type: ignore
[(k, Resource.from_contents(v)) for k, v in self.schema_cache.items()] # type: ignore
[(k, Resource.from_contents(v)) for k, v in self.schema_cache.items()]
)

def get_schema_from_uri(self, schema_uri: str) -> tuple[dict[str, Any], Any]:
Expand Down
2 changes: 1 addition & 1 deletion pystac/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os

__version__ = "1.12.2"
__version__ = "1.13.0"
"""Library version"""


Expand Down
6 changes: 3 additions & 3 deletions tests/extensions/test_eo.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def test_summaries_adds_uri() -> None:
def test_read_pre_09_fields_into_common_metadata() -> None:
eo_item = pystac.Item.from_file(
TestCases.get_path(
"data-files/examples/0.8.1/item-spec/examples/" "landsat8-sample.json"
"data-files/examples/0.8.1/item-spec/examples/landsat8-sample.json"
)
)

Expand All @@ -264,7 +264,7 @@ def test_read_pre_09_fields_into_common_metadata() -> None:
def test_reads_asset_bands_in_pre_1_0_version() -> None:
item = pystac.Item.from_file(
TestCases.get_path(
"data-files/examples/0.9.0/item-spec/examples/" "landsat8-sample.json"
"data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json"
)
)

Expand All @@ -277,7 +277,7 @@ def test_reads_asset_bands_in_pre_1_0_version() -> None:
def test_reads_gsd_in_pre_1_0_version() -> None:
eo_item = pystac.Item.from_file(
TestCases.get_path(
"data-files/examples/0.9.0/item-spec/examples/" "landsat8-sample.json"
"data-files/examples/0.9.0/item-spec/examples/landsat8-sample.json"
)
)

Expand Down
2 changes: 1 addition & 1 deletion tests/serialization/test_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_migrates_removed_extension(self) -> None:
def test_migrates_added_extension(self) -> None:
item = pystac.Item.from_file(
TestCases.get_path(
"data-files/examples/0.8.1/item-spec/" "examples/planet-sample.json"
"data-files/examples/0.8.1/item-spec/examples/planet-sample.json"
)
)
assert ViewExtension.has_extension(item)
Expand Down
70 changes: 35 additions & 35 deletions tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,9 @@ def test_catalog(cat: Catalog) -> None:
c.id for c in children
}, "Children unequal"

assert {c.id for c in root.get_items()} == {
c.id for c in items
}, "Items unequal"
assert {c.id for c in root.get_items()} == {c.id for c in items}, (
"Items unequal"
)

assert actual_catalog_iterations == expected_catalog_iterations

Expand Down Expand Up @@ -460,10 +460,10 @@ def test_clone_generates_correct_links(self, catalog: Catalog) -> None:
actual_counts = actual_link_types_to_counts[obj_id]
assert set(expected_counts.keys()) == set(actual_counts.keys())
for rel in expected_counts:
assert (
actual_counts[rel] == expected_counts[rel]
), "Clone of {} has {} {} links, original has {}".format(
obj_id, actual_counts[rel], rel, expected_counts[rel]
assert actual_counts[rel] == expected_counts[rel], (
"Clone of {} has {} {} links, original has {}".format(
obj_id, actual_counts[rel], rel, expected_counts[rel]
)
)

def test_save_uses_previous_catalog_type(self) -> None:
Expand Down Expand Up @@ -554,12 +554,12 @@ def test_subcatalogs_saved_to_correct_path(self) -> None:

# Check the root catalog path
expected_root_catalog_path = os.path.join(tmp_dir, "catalog.json")
assert os.path.exists(
expected_root_catalog_path
), f"{expected_root_catalog_path} does not exist."
assert os.path.isfile(
expected_root_catalog_path
), f"{expected_root_catalog_path} is not a file."
assert os.path.exists(expected_root_catalog_path), (
f"{expected_root_catalog_path} does not exist."
)
assert os.path.isfile(expected_root_catalog_path), (
f"{expected_root_catalog_path} is not a file."
)

# Check each child catalog
for child_catalog in catalog.get_children():
Expand All @@ -571,12 +571,12 @@ def test_subcatalogs_saved_to_correct_path(self) -> None:
expected_root_catalog_path,
start_is_dir=False,
)
assert os.path.exists(
expected_child_path
), f"{expected_child_path} does not exist."
assert os.path.isfile(
expected_child_path
), f"{expected_child_path} is not a file."
assert os.path.exists(expected_child_path), (
f"{expected_child_path} does not exist."
)
assert os.path.isfile(expected_child_path), (
f"{expected_child_path} is not a file."
)

# Check each item
for item in catalog.get_items(recursive=True):
Expand All @@ -588,12 +588,12 @@ def test_subcatalogs_saved_to_correct_path(self) -> None:
expected_root_catalog_path,
start_is_dir=False,
)
assert os.path.exists(
expected_item_path
), f"{expected_item_path} does not exist."
assert os.path.isfile(
expected_item_path
), f"{expected_item_path} is not a file."
assert os.path.exists(expected_item_path), (
f"{expected_item_path} does not exist."
)
assert os.path.isfile(expected_item_path), (
f"{expected_item_path} is not a file."
)

def test_clone_uses_previous_catalog_type(self) -> None:
catalog = TestCases.case_1()
Expand All @@ -613,10 +613,10 @@ def test_normalize_hrefs_sets_all_hrefs(self) -> None:
target_href = cast(pystac.STACObject, link.target).self_href
else:
target_href = link.absolute_href
assert (
"http://example.com" in target_href
), '[{}] {} does not contain "{}"'.format(
link.rel, target_href, "http://example.com"
assert "http://example.com" in target_href, (
'[{}] {} does not contain "{}"'.format(
link.rel, target_href, "http://example.com"
)
)
for item in items:
assert "http://example.com" in item.self_href
Expand Down Expand Up @@ -748,9 +748,9 @@ def test_generate_subcatalogs_can_be_applied_multiple_times(self) -> None:
assert len(result) == 0
catalog.normalize_hrefs("/tmp")
for item in catalog.get_items(recursive=True):
assert (
item.get_self_href() == expected_hrefs[item.id]
), f" for item '{item.id}'"
assert item.get_self_href() == expected_hrefs[item.id], (
f" for item '{item.id}'"
)

def test_generate_subcatalogs_works_after_adding_more_items(self) -> None:
catalog = Catalog(id="test", description="Test")
Expand Down Expand Up @@ -1151,9 +1151,9 @@ def test_self_contained_catalog_collection_item_links(self) -> None:
continue

href = link["href"]
assert not is_absolute_href(
href
), f"Link with rel={link['rel']} is absolute!"
assert not is_absolute_href(href), (
f"Link with rel={link['rel']} is absolute!"
)

def test_full_copy_and_normalize_works_with_created_stac(self) -> None:
cat = TestCases.case_3()
Expand Down
Loading