planetarypy 0.53.2__tar.gz → 0.53.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {planetarypy-0.53.2 → planetarypy-0.53.4}/CHANGELOG.md +13 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/PKG-INFO +1 -1
- {planetarypy-0.53.2 → planetarypy-0.53.4}/pyproject.toml +2 -2
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/cli.py +12 -4
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/utils.py +18 -2
- {planetarypy-0.53.2 → planetarypy-0.53.4}/.gitignore +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/AUTHORS.md +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/LICENSE +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/README.md +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/docs/README.md +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_index_resolver.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_mission_map.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_objects.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_parser.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_pattern_resolver.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_repo.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_resolver.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_schema.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_url_rewrite.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/catalog/_validation.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/config.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/data/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/datetime_format_converters.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/geo.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/go/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/go/ssi.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/mro/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/mro/ctx/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/mro/ctx/ctx_calib.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/mro/ctx/ctx_edr.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/mro/hirise.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/instruments/utils.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/isis/autoseed.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/isis/projected.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/dynamic_index.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/dynamic_url_handlers.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/index_fixes.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/index_labels.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/index_logging.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/index_main.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/static_index.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/pds/utils.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/plotting.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/__init__.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/archived_kernels.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/config.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/generic_kernels.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/pckernels.py +0 -0
- {planetarypy-0.53.2 → planetarypy-0.53.4}/src/planetarypy/spice/spicer.py +0 -0
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.53.4] - 2026-04-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`url_retrieve` is now concurrency-safe.** Previously two processes (e.g. parallel pytest-xdist workers hitting `load_generic_kernels()` via `Spicer("MARS")` / `Spicer("MOON")`) could clobber each other's `.part` scratch file and race on the final `rename()`, producing `FileNotFoundError`. The scratch file now includes the writer's PID (`{name}.{pid}.part`), and when a concurrent winner has already moved the final file into place the loser silently drops its scratch copy instead of raising. This was the root cause of intermittent CI failures in `test_spicer`.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- CI workflow `test.yaml` now prefetches the SPICE generic kernels in a single-writer step before invoking `pytest`, so parallel test workers see cached files and never trigger the download path simultaneously. Complements the `url_retrieve` fix; either alone would green CI, both together harden the library for any concurrent caller.
|
|
15
|
+
|
|
16
|
+
## [0.53.3] - 2026-04-24
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **`plp fetch` and `plp hibrowse` now emit only the resolved file path on stdout.** Diagnostic lines ("Resolving…", "URL:…", "Fetching…") and the "Browse:" prefix previously mixed with the final path on stdout, which made shell command substitution clumsy (e.g. `qgis (plp fetch mro.ctx.edr <pid>)` captured all of it as arguments). Diagnostics now go to stderr; only the payload path hits stdout.
|
|
20
|
+
|
|
8
21
|
## [0.53.2] - 2026-04-24
|
|
9
22
|
|
|
10
23
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: planetarypy
|
|
3
|
-
Version: 0.53.
|
|
3
|
+
Version: 0.53.4
|
|
4
4
|
Summary: Core package for planetary data tools. Includes PDS index utilities, SPICE integrations, and more.
|
|
5
5
|
Project-URL: Homepage, https://github.com/planetarypy/planetarypy
|
|
6
6
|
Project-URL: Repository, https://github.com/planetarypy/planetarypy
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "planetarypy"
|
|
7
|
-
version = "0.53.
|
|
7
|
+
version = "0.53.4"
|
|
8
8
|
description = "Core package for planetary data tools. Includes PDS index utilities, SPICE integrations, and more."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">= 3.11, <4"
|
|
@@ -127,6 +127,6 @@ max-line-length = 88
|
|
|
127
127
|
extend-ignore = ["E203", "E701"]
|
|
128
128
|
|
|
129
129
|
[tool.bumpversion]
|
|
130
|
-
current_version = "0.53.
|
|
130
|
+
current_version = "0.53.4"
|
|
131
131
|
commit = true
|
|
132
132
|
tag = true
|
|
@@ -64,11 +64,11 @@ def fetch(
|
|
|
64
64
|
|
|
65
65
|
mission, instrument, product_key = key.split(".")
|
|
66
66
|
|
|
67
|
-
typer.echo(f"Resolving {key} / {product_id}...")
|
|
67
|
+
typer.echo(f"Resolving {key} / {product_id}...", err=True)
|
|
68
68
|
try:
|
|
69
69
|
resolved = resolve_product(mission, instrument, product_key, product_id)
|
|
70
70
|
for f in resolved.files:
|
|
71
|
-
typer.echo(f"URL: {resolved.url_stem}/{f}")
|
|
71
|
+
typer.echo(f"URL: {resolved.url_stem}/{f}", err=True)
|
|
72
72
|
if here:
|
|
73
73
|
local_dir = Path.cwd()
|
|
74
74
|
else:
|
|
@@ -76,6 +76,9 @@ def fetch(
|
|
|
76
76
|
mission, instrument, product_key, resolved.product_id,
|
|
77
77
|
)
|
|
78
78
|
download_product(resolved, local_dir, label_only=label_only, force=force)
|
|
79
|
+
# Final resolved paths go to stdout (and only stdout) so that
|
|
80
|
+
# shell command substitution — e.g. `qgis (plp fetch …)` —
|
|
81
|
+
# captures just the file paths, not the diagnostic chatter.
|
|
79
82
|
for f in resolved.files:
|
|
80
83
|
typer.echo(local_dir / f)
|
|
81
84
|
except Exception as e:
|
|
@@ -127,12 +130,17 @@ def hibrowse(
|
|
|
127
130
|
obs_id = f"{parts[0]}_{parts[1]}_{parts[2]}"
|
|
128
131
|
orbit = int(parts[1])
|
|
129
132
|
suffix = "abrowse.jpg" if annotated else "browse.jpg"
|
|
130
|
-
typer.echo(
|
|
133
|
+
typer.echo(
|
|
134
|
+
f"Fetching {HIRISE_BASE}/EXTRAS/{data_level}/{parts[0]}/{_orbit_range(orbit)}/{obs_id}/{pid}.{suffix}",
|
|
135
|
+
err=True,
|
|
136
|
+
)
|
|
131
137
|
|
|
132
138
|
try:
|
|
133
139
|
dest = Path.cwd() if here else None
|
|
134
140
|
outpath = get_browse(product_id, annotated=annotated, dest=dest, force=force)
|
|
135
|
-
|
|
141
|
+
# Raw path on stdout so `qgis (plp hibrowse …)` and similar
|
|
142
|
+
# shell substitutions capture just the path.
|
|
143
|
+
typer.echo(outpath)
|
|
136
144
|
except Exception as e:
|
|
137
145
|
typer.echo(f"Error: {e}", err=True)
|
|
138
146
|
raise typer.Exit(1)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import email.utils as eut
|
|
4
4
|
import http.client as httplib
|
|
5
|
+
import os
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
7
8
|
from urllib.request import Request, urlopen
|
|
@@ -276,7 +277,10 @@ def url_retrieve(
|
|
|
276
277
|
"""
|
|
277
278
|
url = str(url)
|
|
278
279
|
outfile = Path(outfile)
|
|
279
|
-
|
|
280
|
+
# Per-PID scratch file so concurrent callers (e.g. parallel pytest
|
|
281
|
+
# workers, multiprocessing) don't clobber each other's partial
|
|
282
|
+
# writes and don't race on the final rename.
|
|
283
|
+
part_file = outfile.with_suffix(f"{outfile.suffix}.{os.getpid()}.part")
|
|
280
284
|
|
|
281
285
|
if user:
|
|
282
286
|
auth = HTTPBasicAuth(user, passwd)
|
|
@@ -301,7 +305,19 @@ def url_retrieve(
|
|
|
301
305
|
) as fd:
|
|
302
306
|
for chunk in R.iter_content(chunk_size=chunk_size):
|
|
303
307
|
fd.write(chunk)
|
|
304
|
-
|
|
308
|
+
# If another concurrent writer already finished first, drop our
|
|
309
|
+
# scratch file rather than overwriting the winner.
|
|
310
|
+
if outfile.exists():
|
|
311
|
+
part_file.unlink(missing_ok=True)
|
|
312
|
+
else:
|
|
313
|
+
try:
|
|
314
|
+
part_file.replace(outfile)
|
|
315
|
+
except FileNotFoundError:
|
|
316
|
+
# Lost a very tight race: another writer moved our part
|
|
317
|
+
# file out from under us (unlikely with per-PID suffix).
|
|
318
|
+
# If outfile now exists we're still fine.
|
|
319
|
+
if not outfile.exists():
|
|
320
|
+
raise
|
|
305
321
|
|
|
306
322
|
|
|
307
323
|
def have_internet() -> bool:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|