Skip to content

Commit

Permalink
feat: include fields-normalized.json in package
Browse files Browse the repository at this point in the history
  • Loading branch information
jsignell authored and gadomski committed Mar 22, 2023
1 parent f7398bc commit 3be47b2
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 37 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## [Unreleased]

### Changed

- Include a copy of the `fields.json` file (for summaries) with each distribution of PySTAC ([#1045](https://github.com/stac-utils/pystac/pull/1045))

### Deprecated

- `pystac.summaries.FIELDS_JSON_URL` ([#1045](https://github.com/stac-utils/pystac/pull/1045))

### [v1.7.1]

### Changed
Expand Down
19 changes: 10 additions & 9 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,28 @@ This is a checklist to use when releasing a new PySTAC version.

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.
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`).
3. Update the `__version__` attribute in `pystac/version.py` with the new version.
4. Update the CHANGELOG.
3. Pull fields-normalized.json from cdn: run `scripts/pull-static`. Note you will need to have [jq](https://stedolan.github.io/jq/) installed.
4. Update the `__version__` attribute in `pystac/version.py` with the new version.
5. Update the CHANGELOG.
- Create a new header below `## [Unreleased]` with the new version.
- Remove any unused header sections.
- Update the links at the bottom of the page for the new header.
- Audit the CHANGELOG for correctness and readability.
5. Audit the changes.
6. Audit the changes.
Use the CHANGELOG, your favorite diff tool, and the merged Github pull requests to ensure that:
- All notable changes are captured in the CHANGELOG.
- 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.
- All deprecated items that were marked for removal in this version are removed.
6. Craft draft release notes (<https://github.com/stac-utils/pystac/releases/new>).
7. Craft draft release notes (<https://github.com/stac-utils/pystac/releases/new>).
These should be short, readable, and call out any significant changes, especially changes in default behavior or significant new features.
These should also include a link back to the Github milestone for this release, if there is one.
These should _not_ be a complete listing of changes -- those will be auto-generated later, after the tag is pushed.
7. Commit your changes, push your branch to Github, and request a review.
8. Once approved, merge the PR.
9. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`.
8. Commit your changes, push your branch to Github, and request a review.
9. Once approved, merge the PR.
10. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`.
Prefer a signed tag, if possible.
Push the tag to Github.
10. Use the tag to finish your release notes, and publish those.
11. Use the tag to finish your release notes, and publish those.
The "auto generate" feature is your friend, here.
When the release is published, this will trigger the build and release on PyPI.
11. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media.
12. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media.
Empty file added pystac/static/__init__.py
Empty file.
1 change: 1 addition & 0 deletions pystac/static/fields-normalized.json

Large diffs are not rendered by default.

51 changes: 33 additions & 18 deletions pystac/summaries.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import json
import numbers
import sys
from abc import abstractmethod
from copy import deepcopy
from enum import Enum
Expand All @@ -18,6 +20,11 @@
Union,
)

if sys.version_info[:2] < (3, 9):
from importlib_resources import files as importlib_resources_files
else:
from importlib.resources import files as importlib_resources_files

import pystac
from pystac.utils import get_required

Expand All @@ -26,6 +33,21 @@
from pystac.item import Item


def __getattr__(name: str) -> Any:
if name == "FIELDS_JSON_URL":
import warnings

warnings.warn(
"FIELDS_JSON_URL is deprecated and will be removed in v2",
DeprecationWarning,
)
return (
"https://cdn.jsdelivr.net/npm/@radiantearth/"
"stac-fields/fields-normalized.json"
)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


class _Comparable_x(Protocol):
"""Protocol for annotating comparable types.
Expand Down Expand Up @@ -84,13 +106,17 @@ def __repr__(self) -> str:
return self.to_dict().__repr__()


FIELDS_JSON_URL = (
"https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json"
)


@lru_cache(maxsize=None)
def _get_fields_json(url: str) -> Dict[str, Any]:
def _get_fields_json(url: Optional[str]) -> Dict[str, Any]:
if url is None:
# Every time pystac is released this file gets pulled from
# https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json
jsonfields: Dict[str, Any] = json.loads(
importlib_resources_files("pystac.static")
.joinpath("fields-normalized.json")
.read_text()
)
return jsonfields
return pystac.StacIO.default().read_json(url)


Expand Down Expand Up @@ -118,18 +144,7 @@ class Summarizer:
summaryfields: Dict[str, SummaryStrategy]

def __init__(self, fields: Optional[str] = None):
fieldspath = fields or FIELDS_JSON_URL
try:
jsonfields = _get_fields_json(fieldspath)
except Exception as e:
if fields is None:
raise Exception(
"Could not read fields definition file at "
f"{fields} or it is invalid.\n"
"Try using a local fields definition file."
)
else:
raise e
jsonfields = _get_fields_json(fields)
self._set_field_definitions(jsonfields)

def _set_field_definitions(self, fields: Dict[str, Any]) -> None:
Expand Down
11 changes: 11 additions & 0 deletions scripts/pull-static
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

set -e

VERSION="1.0.0-rc.1"
SRC="https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields@$VERSION/fields-normalized.json"
HERE=$(dirname "$0")
DEST=$(dirname "$HERE")/pystac/static/fields-normalized.json

echo "Pulling fields-normalized.json from cdn to $DEST"
curl "$SRC" | jq -c '{metadata: .metadata}' > "$DEST"
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
package_data={"": ["py.typed", "*.jinja2"]},
py_modules=[splitext(basename(path))[0] for path in glob("pystac/*.py")],
python_requires=">=3.8",
install_requires=["python-dateutil>=2.7.0"],
install_requires=[
"importlib-resources>=5.12.0; python_version<'3.9'",
"python-dateutil>=2.7.0",
],
extras_require={
"validation": ["jsonschema>=4.0.1"],
"orjson": ["orjson>=3.5"],
Expand Down
18 changes: 9 additions & 9 deletions tests/test_summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ def test_summary_wrong_custom_fields_file(self) -> None:
Summarizer("wrong/path").summarize(coll.get_all_items())
self.assertTrue("No such file or directory" in str(context.exception))

def test_cannot_open_fields_file(self) -> None:
def test_can_open_fields_file_even_with_no_nework(self) -> None:
old_socket = socket.socket
try:

class no_network(socket.socket):
def __init__(self, *args: Any, **kwargs: Any):
raise Exception("Network call blocked")
class no_network(socket.socket):
def __init__(self, *args: Any, **kwargs: Any):
raise Exception("Network call blocked")

socket.socket = no_network # type:ignore

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

def test_summary_empty(self) -> None:
summaries = Summaries.empty()
Expand Down

0 comments on commit 3be47b2

Please sign in to comment.