Skip to content

Commit 3be47b2

Browse files
jsignellgadomski
authored andcommitted
feat: include fields-normalized.json in package
1 parent f7398bc commit 3be47b2

File tree

8 files changed

+76
-37
lines changed

8 files changed

+76
-37
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Include a copy of the `fields.json` file (for summaries) with each distribution of PySTAC ([#1045](https://github.com/stac-utils/pystac/pull/1045))
8+
9+
### Deprecated
10+
11+
- `pystac.summaries.FIELDS_JSON_URL` ([#1045](https://github.com/stac-utils/pystac/pull/1045))
12+
513
### [v1.7.1]
614

715
### Changed

RELEASING.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,28 @@ This is a checklist to use when releasing a new PySTAC version.
44

55
1. Determine the next version. We do not currently have a versioning guide, but <https://github.com/radiantearth/stac-spec/discussions/1184> has some discussion around the topic.
66
2. Create a release branch with the name `release/vX.Y.Z`, where `X.Y.Z` is the next version (e.g. `1.7.0`).
7-
3. Update the `__version__` attribute in `pystac/version.py` with the new version.
8-
4. Update the CHANGELOG.
7+
3. Pull fields-normalized.json from cdn: run `scripts/pull-static`. Note you will need to have [jq](https://stedolan.github.io/jq/) installed.
8+
4. Update the `__version__` attribute in `pystac/version.py` with the new version.
9+
5. Update the CHANGELOG.
910
- Create a new header below `## [Unreleased]` with the new version.
1011
- Remove any unused header sections.
1112
- Update the links at the bottom of the page for the new header.
1213
- Audit the CHANGELOG for correctness and readability.
13-
5. Audit the changes.
14+
6. Audit the changes.
1415
Use the CHANGELOG, your favorite diff tool, and the merged Github pull requests to ensure that:
1516
- All notable changes are captured in the CHANGELOG.
1617
- The type of release is appropriate for the new version number, i.e. if there are breaking changes, the MAJOR version number must be increased.
1718
- All deprecated items that were marked for removal in this version are removed.
18-
6. Craft draft release notes (<https://github.com/stac-utils/pystac/releases/new>).
19+
7. Craft draft release notes (<https://github.com/stac-utils/pystac/releases/new>).
1920
These should be short, readable, and call out any significant changes, especially changes in default behavior or significant new features.
2021
These should also include a link back to the Github milestone for this release, if there is one.
2122
These should _not_ be a complete listing of changes -- those will be auto-generated later, after the tag is pushed.
22-
7. Commit your changes, push your branch to Github, and request a review.
23-
8. Once approved, merge the PR.
24-
9. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`.
23+
8. Commit your changes, push your branch to Github, and request a review.
24+
9. Once approved, merge the PR.
25+
10. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`.
2526
Prefer a signed tag, if possible.
2627
Push the tag to Github.
27-
10. Use the tag to finish your release notes, and publish those.
28+
11. Use the tag to finish your release notes, and publish those.
2829
The "auto generate" feature is your friend, here.
2930
When the release is published, this will trigger the build and release on PyPI.
30-
11. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media.
31+
12. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media.

pystac/static/__init__.py

Whitespace-only changes.

pystac/static/fields-normalized.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

pystac/summaries.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3+
import json
34
import numbers
5+
import sys
46
from abc import abstractmethod
57
from copy import deepcopy
68
from enum import Enum
@@ -18,6 +20,11 @@
1820
Union,
1921
)
2022

23+
if sys.version_info[:2] < (3, 9):
24+
from importlib_resources import files as importlib_resources_files
25+
else:
26+
from importlib.resources import files as importlib_resources_files
27+
2128
import pystac
2229
from pystac.utils import get_required
2330

@@ -26,6 +33,21 @@
2633
from pystac.item import Item
2734

2835

36+
def __getattr__(name: str) -> Any:
37+
if name == "FIELDS_JSON_URL":
38+
import warnings
39+
40+
warnings.warn(
41+
"FIELDS_JSON_URL is deprecated and will be removed in v2",
42+
DeprecationWarning,
43+
)
44+
return (
45+
"https://cdn.jsdelivr.net/npm/@radiantearth/"
46+
"stac-fields/fields-normalized.json"
47+
)
48+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
49+
50+
2951
class _Comparable_x(Protocol):
3052
"""Protocol for annotating comparable types.
3153
@@ -84,13 +106,17 @@ def __repr__(self) -> str:
84106
return self.to_dict().__repr__()
85107

86108

87-
FIELDS_JSON_URL = (
88-
"https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json"
89-
)
90-
91-
92109
@lru_cache(maxsize=None)
93-
def _get_fields_json(url: str) -> Dict[str, Any]:
110+
def _get_fields_json(url: Optional[str]) -> Dict[str, Any]:
111+
if url is None:
112+
# Every time pystac is released this file gets pulled from
113+
# https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json
114+
jsonfields: Dict[str, Any] = json.loads(
115+
importlib_resources_files("pystac.static")
116+
.joinpath("fields-normalized.json")
117+
.read_text()
118+
)
119+
return jsonfields
94120
return pystac.StacIO.default().read_json(url)
95121

96122

@@ -118,18 +144,7 @@ class Summarizer:
118144
summaryfields: Dict[str, SummaryStrategy]
119145

120146
def __init__(self, fields: Optional[str] = None):
121-
fieldspath = fields or FIELDS_JSON_URL
122-
try:
123-
jsonfields = _get_fields_json(fieldspath)
124-
except Exception as e:
125-
if fields is None:
126-
raise Exception(
127-
"Could not read fields definition file at "
128-
f"{fields} or it is invalid.\n"
129-
"Try using a local fields definition file."
130-
)
131-
else:
132-
raise e
147+
jsonfields = _get_fields_json(fields)
133148
self._set_field_definitions(jsonfields)
134149

135150
def _set_field_definitions(self, fields: Dict[str, Any]) -> None:

scripts/pull-static

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
VERSION="1.0.0-rc.1"
6+
SRC="https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields@$VERSION/fields-normalized.json"
7+
HERE=$(dirname "$0")
8+
DEST=$(dirname "$HERE")/pystac/static/fields-normalized.json
9+
10+
echo "Pulling fields-normalized.json from cdn to $DEST"
11+
curl "$SRC" | jq -c '{metadata: .metadata}' > "$DEST"

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
package_data={"": ["py.typed", "*.jinja2"]},
2424
py_modules=[splitext(basename(path))[0] for path in glob("pystac/*.py")],
2525
python_requires=">=3.8",
26-
install_requires=["python-dateutil>=2.7.0"],
26+
install_requires=[
27+
"importlib-resources>=5.12.0; python_version<'3.9'",
28+
"python-dateutil>=2.7.0",
29+
],
2730
extras_require={
2831
"validation": ["jsonschema>=4.0.1"],
2932
"orjson": ["orjson>=3.5"],

tests/test_summaries.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,19 @@ def test_summary_wrong_custom_fields_file(self) -> None:
3636
Summarizer("wrong/path").summarize(coll.get_all_items())
3737
self.assertTrue("No such file or directory" in str(context.exception))
3838

39-
def test_cannot_open_fields_file(self) -> None:
39+
def test_can_open_fields_file_even_with_no_nework(self) -> None:
4040
old_socket = socket.socket
41+
try:
4142

42-
class no_network(socket.socket):
43-
def __init__(self, *args: Any, **kwargs: Any):
44-
raise Exception("Network call blocked")
43+
class no_network(socket.socket):
44+
def __init__(self, *args: Any, **kwargs: Any):
45+
raise Exception("Network call blocked")
4546

46-
socket.socket = no_network # type:ignore
47-
48-
with self.assertRaises(Exception) as context:
47+
socket.socket = no_network # type:ignore
4948
Summarizer()
50-
socket.socket = old_socket # type:ignore
51-
self.assertTrue("Could not read fields definition" in str(context.exception))
49+
finally:
50+
# even if this test fails, it should not break the whole test suite
51+
socket.socket = old_socket # type:ignore
5252

5353
def test_summary_empty(self) -> None:
5454
summaries = Summaries.empty()

0 commit comments

Comments
 (0)