Skip to content

Commit 51ee9d4

Browse files
committed
feat: first v1 test
1 parent b9abe9e commit 51ee9d4

File tree

11 files changed

+428
-4
lines changed

11 files changed

+428
-4
lines changed

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ classifiers = [
2323
"Programming Language :: Python :: 3.13",
2424
]
2525
requires-python = ">=3.10"
26-
dependencies = ["typing-extensions>=4.12.2"]
26+
dependencies = [
27+
"python-dateutil>=2.9.0.post0",
28+
"typing-extensions>=4.12.2",
29+
]
2730

2831
[project.optional-dependencies]
2932
validate = ["jsonschema>=4.23.0", "referencing>=0.36.2"]

src/pystac/extent.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from __future__ import annotations
22

3+
import copy
34
import datetime
45
import warnings
56
from typing import Any, Sequence
67

78
from typing_extensions import Self
89

910
from .constants import DEFAULT_BBOX, DEFAULT_INTERVAL
11+
from .decorators import v2_deprecated
1012
from .errors import StacWarning
1113
from .types import PermissiveBbox, PermissiveInterval
1214

@@ -54,7 +56,19 @@ def from_dict(cls: type[Self], d: dict[str, Any]) -> Self:
5456
"""Creates a new spatial extent from a dictionary."""
5557
return cls(**d)
5658

57-
def __init__(self, bbox: PermissiveBbox | None = None):
59+
@classmethod
60+
@v2_deprecated("Use the constructor instead")
61+
def from_coordinates(
62+
cls: type[Self],
63+
coordinates: list[Any],
64+
extra_fields: dict[str, Any] | None = None,
65+
) -> Self:
66+
if extra_fields:
67+
return cls(coordinates, **extra_fields)
68+
else:
69+
return cls(coordinates)
70+
71+
def __init__(self, bbox: PermissiveBbox | None = None, **kwargs: Any):
5872
"""Creates a new spatial extent."""
5973
self.bbox: Sequence[Sequence[float | int]]
6074
if bbox is None or len(bbox) == 0:
@@ -63,10 +77,13 @@ def __init__(self, bbox: PermissiveBbox | None = None):
6377
self.bbox = bbox # type: ignore
6478
else:
6579
self.bbox = [bbox] # type: ignore
80+
self.extra_fields = kwargs
6681

6782
def to_dict(self) -> dict[str, Any]:
6883
"""Converts this spatial extent to a dictionary."""
69-
return {"bbox": self.bbox}
84+
d = copy.deepcopy(self.extra_fields)
85+
d["bbox"] = self.bbox
86+
return d
7087

7188

7289
class TemporalExtent:
@@ -77,6 +94,11 @@ def from_dict(cls: type[Self], d: dict[str, Any]) -> Self:
7794
"""Creates a new temporal extent from a dictionary."""
7895
return cls(**d)
7996

97+
@classmethod
98+
def from_now(cls: type[Self]) -> Self:
99+
"""Creates a new temporal extent that starts now and has no end time."""
100+
return cls([[datetime.datetime.now(tz=datetime.timezone.utc), None]])
101+
80102
def __init__(
81103
self,
82104
interval: PermissiveInterval | None = None,

src/pystac/stac_object.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .link import Link
2222

2323
if TYPE_CHECKING:
24+
from .catalog import Catalog
2425
from .io import Read, Write
2526

2627

@@ -81,6 +82,9 @@ def from_dict(
8182
d: dict[str, Any],
8283
*,
8384
href: str | None = None,
85+
root: Catalog | None = None, # TODO deprecation warning
86+
migrate: bool = False,
87+
preserve_dict: bool = True, # TODO deprecation warning
8488
reader: Read | None = None,
8589
writer: Write | None = None,
8690
) -> STACObject:

tests/test_extent.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pytest
55

6-
from pystac import StacWarning, TemporalExtent
6+
from pystac import SpatialExtent, StacWarning, TemporalExtent
77

88

99
def test_temporal_with_datetimes() -> None:
@@ -45,3 +45,14 @@ def test_temporal_with_bad_tail() -> None:
4545
)
4646
d = extent.to_dict()
4747
assert d == {"interval": [["2025-02-11T00:00:00Z", None]]}
48+
49+
50+
def test_temporal_from_now() -> None:
51+
extent = TemporalExtent.from_now()
52+
assert isinstance(extent.interval[0][0], str)
53+
assert extent.interval[0][1] is None
54+
55+
56+
def test_spatial_from_coordinates() -> None:
57+
with pytest.warns(FutureWarning):
58+
SpatialExtent.from_coordinates([-180, -90, 180, 90])

tests/test_item.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ def test_warn_include_self_link() -> None:
3737
def test_warn_transform_hrefs() -> None:
3838
with pytest.warns(FutureWarning):
3939
Item("an-id").to_dict(transform_hrefs=True)
40+
41+
42+
def test_from_dict_migrate() -> None:
43+
d = Item("an-id").to_dict()
44+
d["stac_version"] = "1.0.0"
45+
item = Item.from_dict(d, migrate=True)
46+
item.stac_version == "1.1.0"

tests/v1/__init__.py

Whitespace-only changes.

tests/v1/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import json
2+
from typing import Any
3+
4+
import pytest
5+
6+
from .utils import TestCases
7+
8+
9+
@pytest.fixture
10+
def sample_item_dict() -> dict[str, Any]:
11+
m = TestCases.get_path("data-files/item/sample-item.json")
12+
with open(m) as f:
13+
item_dict: dict[str, Any] = json.load(f)
14+
return item_dict
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"type": "Feature",
3+
"stac_version": "1.1.0",
4+
"id": "CS3-20160503_132131_05",
5+
"properties": {
6+
"datetime": "2016-05-03T13:22:30.040000Z",
7+
"title": "A CS3 item",
8+
"license": "PDDL-1.0",
9+
"providers": [
10+
{
11+
"name": "CoolSat",
12+
"roles": [
13+
"producer",
14+
"licensor"
15+
],
16+
"url": "https://cool-sat.com/"
17+
}
18+
]
19+
},
20+
"geometry": {
21+
"type": "Polygon",
22+
"coordinates": [
23+
[
24+
[
25+
-122.308150179,
26+
37.488035566
27+
],
28+
[
29+
-122.597502109,
30+
37.538869539
31+
],
32+
[
33+
-122.576687533,
34+
37.613537207
35+
],
36+
[
37+
-122.2880486,
38+
37.562818007
39+
],
40+
[
41+
-122.308150179,
42+
37.488035566
43+
]
44+
]
45+
]
46+
},
47+
"links": [
48+
{
49+
"rel": "collection",
50+
"href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v0.8.1/collection-spec/examples/sentinel2.json"
51+
}
52+
],
53+
"assets": {
54+
"analytic": {
55+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/analytic.tif",
56+
"title": "4-Band Analytic",
57+
"product": "http://cool-sat.com/catalog/products/analytic.json",
58+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
59+
"roles": [
60+
"data",
61+
"analytic"
62+
]
63+
},
64+
"thumbnail": {
65+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/thumbnail.png",
66+
"title": "Thumbnail",
67+
"type": "image/png",
68+
"roles": [
69+
"thumbnail"
70+
]
71+
}
72+
},
73+
"bbox": [
74+
-122.59750209,
75+
37.48803556,
76+
-122.2880486,
77+
37.613537207
78+
],
79+
"stac_extensions": [],
80+
"collection": "CS3"
81+
}

tests/v1/test_item.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from copy import deepcopy
2+
from typing import Any
3+
4+
from pystac import Item
5+
6+
from . import utils
7+
8+
9+
def test_to_from_dict(sample_item_dict: dict[str, Any]) -> None:
10+
param_dict = deepcopy(sample_item_dict)
11+
12+
utils.assert_to_from_dict(Item, param_dict)
13+
item = Item.from_dict(param_dict)
14+
assert item.id == "CS3-20160503_132131_05"
15+
16+
# test asset creation additional field(s)
17+
assert (
18+
item.assets["analytic"].extra_fields["product"]
19+
== "http://cool-sat.com/catalog/products/analytic.json"
20+
)
21+
assert len(item.assets["thumbnail"].extra_fields) == 0
22+
23+
# test that the parameter is preserved
24+
assert param_dict == sample_item_dict
25+
26+
# assert that the parameter is preserved regardless of preserve_dict
27+
Item.from_dict(param_dict, preserve_dict=False)
28+
assert param_dict == sample_item_dict

0 commit comments

Comments
 (0)