Skip to content

Commit

Permalink
Merge branch 'main' into python-3.12
Browse files Browse the repository at this point in the history
  • Loading branch information
gadomski authored Oct 19, 2023
2 parents c17c353 + 36e84da commit 230a87b
Show file tree
Hide file tree
Showing 139 changed files with 6,103 additions and 5,459 deletions.
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
# Please run `pre-commit run --all-files` when adding or changing entries.

repos:
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
hooks:
- id: pyupgrade
args:
- "--py39-plus"

- repo: local
hooks:
- id: ruff
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
### Changed

- `validate_all` now accepts a `STACObject` (in addition to accepting a dict, which is now deprecated), but prohibits supplying a value for `href`, which must be supplied _only_ when supplying an object as a dict. Once `validate_all` removes support for an object as a dict, the `href` parameter will also be removed. ([#1246](https://github.com/stac-utils/pystac/pull/1246))
- Report `href` when schema url resolution fails ([#1263](https://github.com/stac-utils/pystac/pull/1263))
- Version extension updated to v1.2.0 ([#1262](https://github.com/stac-utils/pystac/pull/1262))

### Fixed

- Typing of `href` arguments ([#1234](https://github.com/stac-utils/pystac/pull/1234))
- Interactions between **pytest-recording** and the validator schema cache ([#1242](https://github.com/stac-utils/pystac/pull/1242))
- Call `registry` when instantiating `Draft7Validator` ([#1240](https://github.com/stac-utils/pystac/pull/1240))
- Migration for the classification, datacube, table, and timestamps extensions ([#1258](https://github.com/stac-utils/pystac/pull/1258))
- Handling of `bboxes` and `intervals` arguments to `SpatialExtent` and `TemporalExtent`, respectively ([#1268](https://github.com/stac-utils/pystac/pull/1268))

### Removed

Expand Down
7 changes: 3 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
Expand All @@ -15,7 +14,7 @@
import os
import subprocess
import sys
from typing import Any, Dict, List
from typing import Any

sys.path.insert(0, os.path.abspath("."))
sys.path.insert(0, os.path.abspath("../"))
Expand Down Expand Up @@ -152,7 +151,7 @@
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
html_sidebars: Dict[str, List[str]] = {"index": []}
html_sidebars: dict[str, list[str]] = {"index": []}


# -- Options for HTMLHelp output ---------------------------------------------
Expand All @@ -163,7 +162,7 @@

# -- Options for LaTeX output ------------------------------------------------

latex_elements: Dict[str, Any] = {
latex_elements: dict[str, Any] = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ test = [
"pytest-mock~=3.10",
"pytest-recording~=0.13.0",
"pytest~=7.3",
"ruff==0.0.292",
"requests-mock~=1.11",
"ruff==0.1.0",
"types-html5lib~=1.1",
"types-orjson~=3.6",
"types-jsonschema~=4.18",
Expand Down
4 changes: 2 additions & 2 deletions pystac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
]

import os
from typing import Any, Dict, Optional
from typing import Any, Optional

from pystac.errors import (
TemplateError,
Expand Down Expand Up @@ -197,7 +197,7 @@ def write_file(


def read_dict(
d: Dict[str, Any],
d: dict[str, Any],
href: Optional[str] = None,
root: Optional[Catalog] = None,
stac_io: Optional[StacIO] = None,
Expand Down
149 changes: 125 additions & 24 deletions pystac/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
import shutil
from copy import copy, deepcopy
from html import escape
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, TypeVar, Union
from typing import TYPE_CHECKING, Any, Protocol, TypeVar

from pystac import common_metadata, utils
from pystac import MediaType, STACError, common_metadata, utils
from pystac.html.jinja_env import get_jinja_env
from pystac.utils import is_absolute_href, make_absolute_href, make_relative_href

if TYPE_CHECKING:
from pystac.collection import Collection
from pystac.common_metadata import CommonMetadata
from pystac.extensions.ext import AssetExt
from pystac.item import Item

A = TypeVar("A", bound="Asset")

Expand Down Expand Up @@ -41,38 +40,38 @@ class Asset:
href: str
"""Link to the asset object. Relative and absolute links are both allowed."""

title: Optional[str]
title: str | None
"""Optional displayed title for clients and users."""

description: Optional[str]
description: str | None
"""A description of the Asset providing additional details, such as how it was
processed or created. CommonMark 0.29 syntax MAY be used for rich text
representation."""

media_type: Optional[str]
media_type: str | None
"""Optional description of the media type. Registered Media Types are preferred.
See :class:`~pystac.MediaType` for common media types."""

roles: Optional[List[str]]
roles: list[str] | None
"""Optional, Semantic roles (i.e. thumbnail, overview, data, metadata) of the
asset."""

owner: Optional[Union[Item, Collection]]
owner: Assets | None
"""The :class:`~pystac.Item` or :class:`~pystac.Collection` that this asset belongs
to, or ``None`` if it has no owner."""

extra_fields: Dict[str, Any]
extra_fields: dict[str, Any]
"""Optional, additional fields for this asset. This is used by extensions as a
way to serialize and deserialize properties on asset object JSON."""

def __init__(
self,
href: str,
title: Optional[str] = None,
description: Optional[str] = None,
media_type: Optional[str] = None,
roles: Optional[List[str]] = None,
extra_fields: Optional[Dict[str, Any]] = None,
title: str | None = None,
description: str | None = None,
media_type: str | None = None,
roles: list[str] | None = None,
extra_fields: dict[str, Any] | None = None,
) -> None:
self.href = utils.make_posix_style(href)
self.title = title
Expand All @@ -84,7 +83,7 @@ def __init__(
# The Item which owns this Asset.
self.owner = None

def set_owner(self, obj: Union[Collection, Item]) -> None:
def set_owner(self, obj: Assets) -> None:
"""Sets the owning item of this Asset.
The owning item will be used to resolve relative HREFs of this asset.
Expand All @@ -94,7 +93,7 @@ def set_owner(self, obj: Union[Collection, Item]) -> None:
"""
self.owner = obj

def get_absolute_href(self) -> Optional[str]:
def get_absolute_href(self) -> str | None:
"""Gets the absolute href for this asset, if possible.
If this Asset has no associated Item, and the asset HREF is a relative path,
Expand All @@ -114,14 +113,14 @@ def get_absolute_href(self) -> Optional[str]:
return utils.make_absolute_href(self.href, item_self)
return None

def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""Returns this Asset as a dictionary.
Returns:
dict: A serialization of the Asset.
"""

d: Dict[str, Any] = {"href": self.href}
d: dict[str, Any] = {"href": self.href}

if self.media_type is not None:
d["type"] = self.media_type
Expand Down Expand Up @@ -179,7 +178,7 @@ def common_metadata(self) -> CommonMetadata:
return common_metadata.CommonMetadata(self)

def __repr__(self) -> str:
return "<Asset href={}>".format(self.href)
return f"<Asset href={self.href}>"

def _repr_html_(self) -> str:
jinja_env = get_jinja_env()
Expand All @@ -190,7 +189,7 @@ def _repr_html_(self) -> str:
return escape(repr(self))

@classmethod
def from_dict(cls: Type[A], d: Dict[str, Any]) -> A:
def from_dict(cls: type[A], d: dict[str, Any]) -> A:
"""Constructs an Asset from a dict.
Returns:
Expand Down Expand Up @@ -275,9 +274,111 @@ def ext(self) -> AssetExt:
return AssetExt(stac_object=self)


def _absolute_href(
href: str, owner: Optional[Union[Item, Collection]], action: str = "access"
) -> str:
class Assets(Protocol):
"""Protocol, with functionality, for STAC objects that have assets."""

assets: dict[str, Asset]
"""The asset dictionary."""

def get_assets(
self,
media_type: str | MediaType | None = None,
role: str | None = None,
) -> dict[str, Asset]:
"""Get this object's assets.
Args:
media_type: If set, filter the assets such that only those with a
matching ``media_type`` are returned.
role: If set, filter the assets such that only those with a matching
``role`` are returned.
Returns:
Dict[str, Asset]: A dictionary of assets that match ``media_type``
and/or ``role`` if set or else all of this object's assets.
"""
return {
k: deepcopy(v)
for k, v in self.assets.items()
if (media_type is None or v.media_type == media_type)
and (role is None or v.has_role(role))
}

def add_asset(self, key: str, asset: Asset) -> None:
"""Adds an Asset to this object.
Args:
key : The unique key of this asset.
asset : The Asset to add.
"""
asset.set_owner(self)
self.assets[key] = asset

def delete_asset(self, key: str) -> None:
"""Deletes the asset at the given key, and removes the asset's data
file from the local filesystem.
It is an error to attempt to delete an asset's file if it is on a
remote filesystem.
To delete the asset without removing the file, use `del item.assets["key"]`.
Args:
key: The unique key of this asset.
"""
asset = self.assets[key]
asset.set_owner(self)
asset.delete()

del self.assets[key]

def make_asset_hrefs_relative(self) -> Assets:
"""Modify each asset's HREF to be relative to this object's self HREF.
Returns:
Item: self
"""
self_href = self.get_self_href()
for asset in self.assets.values():
if is_absolute_href(asset.href):
if self_href is None:
raise STACError(
"Cannot make asset HREFs relative if no self_href is set."
)
asset.href = make_relative_href(asset.href, self_href)
return self

def make_asset_hrefs_absolute(self) -> Assets:
"""Modify each asset's HREF to be absolute.
Any asset HREFs that are relative will be modified to absolute based on this
item's self HREF.
Returns:
Assets: self
"""
self_href = self.get_self_href()
for asset in self.assets.values():
if not is_absolute_href(asset.href):
if self_href is None:
raise STACError(
"Cannot make relative asset HREFs absolute "
"if no self_href is set."
)
asset.href = make_absolute_href(asset.href, self_href)
return self

def get_self_href(self) -> str | None:
"""Abstract definition of STACObject.get_self_href.
Needed to make the `make_asset_hrefs_{absolute|relative}` methods pass
type checking. Refactoring out all the link behavior in STACObject to
its own protocol would be too heavy, so we just use this stub instead.
"""
...


def _absolute_href(href: str, owner: Assets | None, action: str = "access") -> str:
if utils.is_absolute_href(href):
return href
else:
Expand Down
Loading

0 comments on commit 230a87b

Please sign in to comment.