astro-metadata-translator 29.2025.4300__tar.gz → 29.2025.4800__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.
- {astro_metadata_translator-29.2025.4300/python/astro_metadata_translator.egg-info → astro_metadata_translator-29.2025.4800}/PKG-INFO +6 -5
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/pyproject.toml +6 -5
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/bin/translate.py +18 -13
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/bin/writeindex.py +14 -9
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/bin/writesidecar.py +14 -38
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/file_helpers.py +106 -64
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/indexing.py +28 -15
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/tests.py +20 -12
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translator.py +3 -2
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/decam.py +7 -3
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/helpers.py +14 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/megaprime.py +7 -3
- astro_metadata_translator-29.2025.4800/python/astro_metadata_translator/version.py +2 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800/python/astro_metadata_translator.egg-info}/PKG-INFO +6 -5
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/requires.txt +2 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_indexing.py +1 -1
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_translate_header.py +2 -2
- astro_metadata_translator-29.2025.4300/python/astro_metadata_translator/version.py +0 -2
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/CHANGES.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/LICENSE +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/MANIFEST.in +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/__init__.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/bin/__init__.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/cli/__init__.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/cli/astrometadata.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/CFHT/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/DECam/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00042600.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00120200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00120400.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00120600.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00120800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00121000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00121200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00121400.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00121600.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00121800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00122000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00122800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00123000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00123200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00123800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00124000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00124200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00124400.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00124600.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00124800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00186800.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00187000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00187200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00187400.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00187600.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00188000.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/HSC-HSCA00188200.yaml +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/HSC/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/SDSS/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/corrections/SuprimeCam/README.md +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/headers.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/observationGroup.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/observationInfo.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/properties.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/py.typed +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/serialize/__init__.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/serialize/fits.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/__init__.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/fits.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/hsc.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/sdss.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/subaru.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator/translators/suprimecam.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/SOURCES.txt +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/dependency_links.txt +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/entry_points.txt +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/top_level.txt +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/python/astro_metadata_translator.egg-info/zip-safe +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/setup.cfg +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_basics.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_cfht.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_cli.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_decam.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_extensions.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_groups.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_headers.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_sdss.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_shadowing.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_subaru.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_translation.py +0 -0
- {astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/tests/test_translator_helpers.py +0 -0
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astro-metadata-translator
|
|
3
|
-
Version: 29.2025.
|
|
3
|
+
Version: 29.2025.4800
|
|
4
4
|
Summary: A translator for astronomical metadata.
|
|
5
5
|
Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/lsst/astro_metadata_translator
|
|
8
8
|
Keywords: lsst
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
10
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
11
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
12
11
|
Classifier: Operating System :: OS Independent
|
|
13
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
|
|
18
|
-
Requires-Python: >=3.10.0
|
|
17
|
+
Requires-Python: >=3.11.0
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
19
|
License-File: LICENSE
|
|
21
20
|
Requires-Dist: astropy>=3.0.5
|
|
22
21
|
Requires-Dist: pyyaml>=3.13
|
|
22
|
+
Requires-Dist: lsst-resources
|
|
23
|
+
Requires-Dist: fsspec
|
|
23
24
|
Requires-Dist: click>=8
|
|
24
25
|
Provides-Extra: test
|
|
25
26
|
Requires-Dist: pytest; extra == "test"
|
{astro_metadata_translator-29.2025.4300 → astro_metadata_translator-29.2025.4800}/pyproject.toml
RENAMED
|
@@ -4,9 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "astro-metadata-translator"
|
|
7
|
-
requires-python = ">=3.
|
|
7
|
+
requires-python = ">=3.11.0"
|
|
8
8
|
description = "A translator for astronomical metadata."
|
|
9
|
-
license =
|
|
9
|
+
license = "BSD-3-Clause"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
10
11
|
authors = [
|
|
11
12
|
{name="Rubin Observatory Data Management", email="dm-admin@lists.lsst.org"},
|
|
12
13
|
]
|
|
@@ -14,19 +15,20 @@ readme = "README.md"
|
|
|
14
15
|
classifiers = [
|
|
15
16
|
"Intended Audience :: Science/Research",
|
|
16
17
|
"Topic :: Scientific/Engineering :: Astronomy",
|
|
17
|
-
"License :: OSI Approved :: BSD License",
|
|
18
18
|
"Operating System :: OS Independent",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
20
21
|
"Programming Language :: Python :: 3.13",
|
|
21
22
|
"Programming Language :: Python :: 3.12",
|
|
22
23
|
"Programming Language :: Python :: 3.11",
|
|
23
|
-
"Programming Language :: Python :: 3.10",
|
|
24
24
|
]
|
|
25
25
|
keywords = ["lsst"]
|
|
26
26
|
dynamic = ["version"]
|
|
27
27
|
dependencies = [
|
|
28
28
|
"astropy >=3.0.5",
|
|
29
29
|
"pyyaml >=3.13",
|
|
30
|
+
"lsst-resources",
|
|
31
|
+
"fsspec", # Do not include S3/http variants by default.
|
|
30
32
|
"click >= 8"
|
|
31
33
|
]
|
|
32
34
|
|
|
@@ -43,7 +45,6 @@ astrometadata = "astro_metadata_translator.cli.astrometadata:main"
|
|
|
43
45
|
|
|
44
46
|
[tool.setuptools]
|
|
45
47
|
zip-safe = true
|
|
46
|
-
license-files = ["LICENSE"]
|
|
47
48
|
|
|
48
49
|
[tool.setuptools.packages.find]
|
|
49
50
|
where = ["python"]
|
|
@@ -23,18 +23,22 @@ import math
|
|
|
23
23
|
import traceback
|
|
24
24
|
from collections import defaultdict
|
|
25
25
|
from collections.abc import Sequence
|
|
26
|
-
from typing import IO, Any
|
|
26
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
27
27
|
|
|
28
28
|
import astropy.time
|
|
29
29
|
import astropy.units as u
|
|
30
30
|
import yaml
|
|
31
31
|
from astropy.table import Column, MaskedColumn, QTable
|
|
32
|
+
from lsst.resources import ResourcePath
|
|
32
33
|
|
|
33
34
|
from astro_metadata_translator import MetadataTranslator, ObservationInfo, fix_header
|
|
34
35
|
|
|
35
36
|
from ..file_helpers import find_files, read_basic_metadata_from_file
|
|
36
37
|
from ..properties import PROPERTIES
|
|
37
38
|
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from lsst.resources import ResourcePathExpression
|
|
41
|
+
|
|
38
42
|
log = logging.getLogger(__name__)
|
|
39
43
|
|
|
40
44
|
# Output mode choices
|
|
@@ -74,7 +78,7 @@ TABLE_COLUMNS = (
|
|
|
74
78
|
|
|
75
79
|
|
|
76
80
|
def read_file(
|
|
77
|
-
file:
|
|
81
|
+
file: ResourcePathExpression,
|
|
78
82
|
hdrnum: int,
|
|
79
83
|
print_trace: bool,
|
|
80
84
|
output_columns: defaultdict[str, list],
|
|
@@ -86,7 +90,7 @@ def read_file(
|
|
|
86
90
|
|
|
87
91
|
Parameters
|
|
88
92
|
----------
|
|
89
|
-
file : `str`
|
|
93
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
90
94
|
The file from which the header is to be read.
|
|
91
95
|
hdrnum : `int`
|
|
92
96
|
The HDU number to read. The primary header is always read and
|
|
@@ -127,10 +131,11 @@ def read_file(
|
|
|
127
131
|
if output_mode != "table":
|
|
128
132
|
log.info("Analyzing %s...", file)
|
|
129
133
|
|
|
134
|
+
uri = ResourcePath(file, forceDirectory=False)
|
|
130
135
|
try:
|
|
131
|
-
md = read_basic_metadata_from_file(
|
|
136
|
+
md = read_basic_metadata_from_file(uri, hdrnum, can_raise=True)
|
|
132
137
|
if md is None:
|
|
133
|
-
raise RuntimeError(f"Failed to read file {
|
|
138
|
+
raise RuntimeError(f"Failed to read file {uri} HDU={hdrnum}")
|
|
134
139
|
|
|
135
140
|
if output_mode.endswith("native"):
|
|
136
141
|
# Strip native and don't change type of md
|
|
@@ -141,21 +146,21 @@ def read_file(
|
|
|
141
146
|
|
|
142
147
|
if output_mode in ("yaml", "fixed"):
|
|
143
148
|
if output_mode == "fixed":
|
|
144
|
-
fix_header(md, filename=
|
|
149
|
+
fix_header(md, filename=str(uri))
|
|
145
150
|
|
|
146
151
|
# The header should be written out in the insertion order
|
|
147
152
|
print(yaml.dump(md, sort_keys=False), file=outstream)
|
|
148
153
|
return True
|
|
149
154
|
|
|
150
155
|
# Try to work out a translator class.
|
|
151
|
-
translator_class = MetadataTranslator.determine_translator(md, filename=
|
|
156
|
+
translator_class = MetadataTranslator.determine_translator(md, filename=str(uri))
|
|
152
157
|
|
|
153
158
|
# Work out which headers to translate, assuming the default if
|
|
154
159
|
# we have a YAML test file.
|
|
155
|
-
if
|
|
160
|
+
if uri.getExtension() == ".yaml":
|
|
156
161
|
headers = [md]
|
|
157
162
|
else:
|
|
158
|
-
headers = list(translator_class.determine_translatable_headers(
|
|
163
|
+
headers = list(translator_class.determine_translatable_headers(uri, md))
|
|
159
164
|
if output_mode == "auto":
|
|
160
165
|
output_mode = "table" if len(headers) > 1 else "verbose"
|
|
161
166
|
|
|
@@ -163,7 +168,7 @@ def read_file(
|
|
|
163
168
|
raise ValueError("Table mode requires output columns")
|
|
164
169
|
|
|
165
170
|
for md in headers:
|
|
166
|
-
obs_info = ObservationInfo(md, pedantic=False, filename=
|
|
171
|
+
obs_info = ObservationInfo(md, pedantic=False, filename=str(uri))
|
|
167
172
|
if output_mode == "table":
|
|
168
173
|
for c in TABLE_COLUMNS:
|
|
169
174
|
output_columns[c["label"]].append(getattr(obs_info, c["attr"]))
|
|
@@ -178,7 +183,7 @@ def read_file(
|
|
|
178
183
|
if print_trace:
|
|
179
184
|
traceback.print_exc(file=outstream)
|
|
180
185
|
else:
|
|
181
|
-
print(f"Failure processing {
|
|
186
|
+
print(f"Failure processing {uri}: {e}", file=outstream)
|
|
182
187
|
return False
|
|
183
188
|
return True
|
|
184
189
|
|
|
@@ -308,9 +313,9 @@ def translate_or_dump_headers(
|
|
|
308
313
|
isok = read_file(path, hdrnum, print_trace, output_columns, outstream, output_mode, heading)
|
|
309
314
|
heading = False
|
|
310
315
|
if isok:
|
|
311
|
-
okay.append(path)
|
|
316
|
+
okay.append(str(path))
|
|
312
317
|
else:
|
|
313
|
-
failed.append(path)
|
|
318
|
+
failed.append(str(path))
|
|
314
319
|
|
|
315
320
|
# Check if we have exceeded the page size and should dump the table.
|
|
316
321
|
if output_mode == "table" and len(output_columns[TABLE_COLUMNS[0]["label"]]) >= MAX_TABLE_PAGE_SIZE:
|
|
@@ -19,6 +19,8 @@ import os
|
|
|
19
19
|
from collections.abc import MutableMapping, Sequence
|
|
20
20
|
from typing import IO
|
|
21
21
|
|
|
22
|
+
from lsst.resources import ResourcePath
|
|
23
|
+
|
|
22
24
|
from ..file_helpers import find_files
|
|
23
25
|
from ..indexing import index_files
|
|
24
26
|
|
|
@@ -87,15 +89,19 @@ def write_index_files(
|
|
|
87
89
|
|
|
88
90
|
failed = []
|
|
89
91
|
okay = []
|
|
90
|
-
files_per_directory: MutableMapping[
|
|
92
|
+
files_per_directory: MutableMapping[ResourcePath, list[ResourcePath]] = {}
|
|
91
93
|
|
|
92
94
|
# Group each file by directory if no explicit output path
|
|
93
95
|
if outpath is None:
|
|
94
96
|
for path in found_files:
|
|
95
|
-
head, tail =
|
|
96
|
-
files_per_directory.setdefault(head, []).append(tail)
|
|
97
|
+
head, tail = path.split()
|
|
98
|
+
files_per_directory.setdefault(head, []).append(ResourcePath(tail, forceAbsolute=False))
|
|
97
99
|
else:
|
|
98
|
-
|
|
100
|
+
# We want the requested files to be paths relative to the current
|
|
101
|
+
# directory. For now this assumes that all the input files are
|
|
102
|
+
# local -- we are not trying to discover a shared root directory.
|
|
103
|
+
cwd = ResourcePath(".", forceAbsolute=True, forceDirectory=True)
|
|
104
|
+
files_per_directory[cwd] = list(found_files)
|
|
99
105
|
|
|
100
106
|
# Extract translated metadata for each file in each directory
|
|
101
107
|
for directory, files_in_dir in files_per_directory.items():
|
|
@@ -113,11 +119,10 @@ def write_index_files(
|
|
|
113
119
|
|
|
114
120
|
# Write the index file
|
|
115
121
|
if outpath is None:
|
|
116
|
-
index_file =
|
|
122
|
+
index_file = directory.join("_index.json")
|
|
117
123
|
else:
|
|
118
|
-
index_file = outpath
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
log.info("Wrote index file to %s", index_file)
|
|
124
|
+
index_file = ResourcePath(outpath, forceAbsolute=False)
|
|
125
|
+
index_file.write(json.dumps(output).encode())
|
|
126
|
+
log.info("Wrote index file to %s", index_file)
|
|
122
127
|
|
|
123
128
|
return okay, failed
|
|
@@ -14,45 +14,22 @@ from __future__ import annotations
|
|
|
14
14
|
__all__ = ("write_sidecar_file", "write_sidecar_files")
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
-
import os
|
|
18
17
|
import traceback
|
|
19
18
|
from collections.abc import Sequence
|
|
20
|
-
from typing import IO
|
|
19
|
+
from typing import IO, TYPE_CHECKING
|
|
21
20
|
|
|
22
|
-
from
|
|
23
|
-
|
|
24
|
-
log = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _split_ext(file: str) -> tuple[str, str]:
|
|
28
|
-
"""Split the extension from the file name and return it and the root.
|
|
29
|
-
|
|
30
|
-
Parameters
|
|
31
|
-
----------
|
|
32
|
-
file : `str`
|
|
33
|
-
The file name to examine.
|
|
21
|
+
from lsst.resources import ResourcePath
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
-------
|
|
37
|
-
root : `str`
|
|
38
|
-
The root of the file name.
|
|
39
|
-
ext : `str`
|
|
40
|
-
The file extension. There is special case handling of .gz and other
|
|
41
|
-
compression extensions.
|
|
42
|
-
"""
|
|
43
|
-
special = {".gz", ".bz2", ".xz", ".fz"}
|
|
44
|
-
|
|
45
|
-
root, ext = os.path.splitext(file)
|
|
23
|
+
from ..file_helpers import find_files, read_file_info
|
|
46
24
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
ext = second_ext + ext
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from lsst.resources import ResourcePathExpression
|
|
50
27
|
|
|
51
|
-
|
|
28
|
+
log = logging.getLogger(__name__)
|
|
52
29
|
|
|
53
30
|
|
|
54
31
|
def write_sidecar_file(
|
|
55
|
-
file:
|
|
32
|
+
file: ResourcePathExpression,
|
|
56
33
|
hdrnum: int,
|
|
57
34
|
content_mode: str,
|
|
58
35
|
print_trace: bool,
|
|
@@ -62,7 +39,7 @@ def write_sidecar_file(
|
|
|
62
39
|
|
|
63
40
|
Parameters
|
|
64
41
|
----------
|
|
65
|
-
file : `str`
|
|
42
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
66
43
|
The file from which the header is to be read.
|
|
67
44
|
hdrnum : `int`
|
|
68
45
|
The HDU number to read. The primary header is always read and
|
|
@@ -103,12 +80,11 @@ def write_sidecar_file(
|
|
|
103
80
|
return False
|
|
104
81
|
|
|
105
82
|
# Calculate sidecar file name derived from this file.
|
|
106
|
-
#
|
|
83
|
+
# .fits.gz should be replaced
|
|
107
84
|
# with .json, and not resulting in .fits.json.
|
|
108
|
-
|
|
109
|
-
newfile =
|
|
110
|
-
|
|
111
|
-
print(json_str, file=fd)
|
|
85
|
+
uri = ResourcePath(file)
|
|
86
|
+
newfile = uri.updatedExtension(".json")
|
|
87
|
+
newfile.write(str(json_str).encode())
|
|
112
88
|
log.debug("Writing sidecar file %s", newfile)
|
|
113
89
|
|
|
114
90
|
except Exception as e:
|
|
@@ -169,8 +145,8 @@ def write_sidecar_files(
|
|
|
169
145
|
for path in sorted(found_files):
|
|
170
146
|
isok = write_sidecar_file(path, hdrnum, content_mode, print_trace, outstream)
|
|
171
147
|
if isok:
|
|
172
|
-
okay.append(path)
|
|
148
|
+
okay.append(str(path))
|
|
173
149
|
else:
|
|
174
|
-
failed.append(path)
|
|
150
|
+
failed.append(str(path))
|
|
175
151
|
|
|
176
152
|
return okay, failed
|
|
@@ -20,16 +20,21 @@ __all__ = ("find_files", "read_basic_metadata_from_file", "read_file_info")
|
|
|
20
20
|
|
|
21
21
|
import json
|
|
22
22
|
import logging
|
|
23
|
-
import os
|
|
24
23
|
import re
|
|
25
24
|
import traceback
|
|
26
25
|
from collections.abc import Iterable, MutableMapping
|
|
27
|
-
from typing import IO, Any
|
|
26
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
27
|
+
|
|
28
|
+
from astropy.io import fits
|
|
29
|
+
from lsst.resources import ResourcePath
|
|
28
30
|
|
|
29
31
|
from .headers import merge_headers
|
|
30
32
|
from .observationInfo import ObservationInfo
|
|
31
33
|
from .tests import read_test_file
|
|
32
34
|
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from lsst.resources import ResourcePathExpression
|
|
37
|
+
|
|
33
38
|
log = logging.getLogger(__name__)
|
|
34
39
|
|
|
35
40
|
# Prefer afw over Astropy
|
|
@@ -37,30 +42,15 @@ try:
|
|
|
37
42
|
import lsst.daf.base # noqa: F401 need PropertyBase for readMetadata
|
|
38
43
|
from lsst.afw.fits import FitsError, readMetadata
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
can_raise : `bool`, optional
|
|
50
|
-
Indicate whether the function can raise and exception (default)
|
|
51
|
-
or should return `None` on error. Can still raise if an unexpected
|
|
52
|
-
error is encountered.
|
|
53
|
-
|
|
54
|
-
Returns
|
|
55
|
-
-------
|
|
56
|
-
md : `dict`
|
|
57
|
-
The requested header. `None` if it could not be read and
|
|
58
|
-
``can_raise`` is `False`.
|
|
59
|
-
|
|
60
|
-
Notes
|
|
61
|
-
-----
|
|
62
|
-
Tries to catch a FitsError 104 and convert to `FileNotFoundError`.
|
|
63
|
-
"""
|
|
45
|
+
have_afw = True
|
|
46
|
+
|
|
47
|
+
def _read_fits_metadata_afw(
|
|
48
|
+
file: str, hdu: int, can_raise: bool = False
|
|
49
|
+
) -> MutableMapping[str, Any] | None:
|
|
50
|
+
# Only works with local files.
|
|
51
|
+
# Tries to catch a FitsError 104 and convert to `FileNotFoundError`.
|
|
52
|
+
# For detailed docstrings see the _read_fits_metadata implementation
|
|
53
|
+
# below.
|
|
64
54
|
try:
|
|
65
55
|
return readMetadata(file, hdu=hdu)
|
|
66
56
|
except FitsError as e:
|
|
@@ -72,31 +62,84 @@ try:
|
|
|
72
62
|
return None
|
|
73
63
|
|
|
74
64
|
except ImportError:
|
|
75
|
-
|
|
65
|
+
have_afw = False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _read_fits_metadata_astropy(
|
|
69
|
+
file: ResourcePathExpression, hdu: int, can_raise: bool = False
|
|
70
|
+
) -> MutableMapping[str, Any] | None:
|
|
71
|
+
"""Read a FITS header using astropy.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
76
|
+
The file to read.
|
|
77
|
+
hdu : `int`
|
|
78
|
+
The header number to read.
|
|
79
|
+
can_raise : `bool`, optional
|
|
80
|
+
Indicate whether the function can raise and exception (default)
|
|
81
|
+
or should return `None` on error. Can still raise if an unexpected
|
|
82
|
+
error is encountered.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
md : `dict`
|
|
87
|
+
The requested header. `None` if it could not be read and
|
|
88
|
+
``can_raise`` is `False`.
|
|
89
|
+
"""
|
|
90
|
+
header = None
|
|
91
|
+
uri = ResourcePath(file, forceDirectory=False)
|
|
92
|
+
try:
|
|
93
|
+
fs, fspath = uri.to_fsspec()
|
|
94
|
+
with fs.open(fspath) as f, fits.open(f) as fits_file:
|
|
95
|
+
try:
|
|
96
|
+
# Copy forces a download of the remote resource.
|
|
97
|
+
header = fits_file[hdu].header.copy()
|
|
98
|
+
except IndexError as e:
|
|
99
|
+
if can_raise:
|
|
100
|
+
raise e
|
|
101
|
+
except Exception as e:
|
|
102
|
+
if can_raise:
|
|
103
|
+
raise e
|
|
104
|
+
return header
|
|
76
105
|
|
|
77
|
-
def _read_fits_metadata(file: str, hdu: int, can_raise: bool = False) -> MutableMapping[str, Any] | None:
|
|
78
|
-
"""Read a FITS header using astropy."""
|
|
79
|
-
# For detailed docstrings see the afw implementation above
|
|
80
|
-
header = None
|
|
81
|
-
try:
|
|
82
|
-
with fits.open(file) as fits_file:
|
|
83
|
-
try:
|
|
84
|
-
header = fits_file[hdu].header
|
|
85
|
-
except IndexError as e:
|
|
86
|
-
if can_raise:
|
|
87
|
-
raise e
|
|
88
|
-
except Exception as e:
|
|
89
|
-
if can_raise:
|
|
90
|
-
raise e
|
|
91
|
-
return header
|
|
92
106
|
|
|
107
|
+
def _read_fits_metadata(
|
|
108
|
+
file: ResourcePathExpression, hdu: int, can_raise: bool = False
|
|
109
|
+
) -> MutableMapping[str, Any] | None:
|
|
110
|
+
"""Read a FITS header using afw or astropy.
|
|
111
|
+
|
|
112
|
+
Prefer afw for local reads if available.
|
|
93
113
|
|
|
94
|
-
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
117
|
+
The file to read.
|
|
118
|
+
hdu : `int`
|
|
119
|
+
The header number to read.
|
|
120
|
+
can_raise : `bool`, optional
|
|
121
|
+
Indicate whether the function can raise and exception (default)
|
|
122
|
+
or should return `None` on error. Can still raise if an unexpected
|
|
123
|
+
error is encountered.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
md : `dict`
|
|
128
|
+
The requested header. `None` if it could not be read and
|
|
129
|
+
``can_raise`` is `False`.
|
|
130
|
+
"""
|
|
131
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
132
|
+
if have_afw and uri.isLocal:
|
|
133
|
+
return _read_fits_metadata_afw(uri.ospath, hdu, can_raise=can_raise)
|
|
134
|
+
return _read_fits_metadata_astropy(uri, hdu, can_raise=can_raise)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def find_files(files: Iterable[ResourcePathExpression], regex: str) -> list[ResourcePath]:
|
|
95
138
|
"""Find files for processing.
|
|
96
139
|
|
|
97
140
|
Parameters
|
|
98
141
|
----------
|
|
99
|
-
files : iterable of `
|
|
142
|
+
files : iterable of `lsst.resources.ResourcePathExpression`
|
|
100
143
|
The files or directories from which the headers are to be read.
|
|
101
144
|
regex : `str`
|
|
102
145
|
Regular expression string used to filter files when a directory is
|
|
@@ -104,34 +147,31 @@ def find_files(files: Iterable[str], regex: str) -> list[str]:
|
|
|
104
147
|
|
|
105
148
|
Returns
|
|
106
149
|
-------
|
|
107
|
-
found_files : `list` of `
|
|
150
|
+
found_files : `list` of `lsst.resources.ResourcePath`
|
|
108
151
|
The files that were found.
|
|
109
152
|
"""
|
|
110
153
|
file_regex = re.compile(regex)
|
|
111
|
-
found_files = []
|
|
154
|
+
found_files: list[ResourcePath] = []
|
|
112
155
|
|
|
113
156
|
# Find all the files of interest
|
|
114
|
-
for
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
path = os.path.join(root, name)
|
|
119
|
-
if os.path.isfile(path) and file_regex.search(name):
|
|
120
|
-
found_files.append(path)
|
|
157
|
+
for candidate in files:
|
|
158
|
+
uri = ResourcePath(candidate, forceAbsolute=False)
|
|
159
|
+
if uri.isdir():
|
|
160
|
+
found_files.extend(ResourcePath.findFileResources([uri], file_filter=file_regex, grouped=False))
|
|
121
161
|
else:
|
|
122
|
-
found_files.append(
|
|
162
|
+
found_files.append(uri)
|
|
123
163
|
|
|
124
164
|
return found_files
|
|
125
165
|
|
|
126
166
|
|
|
127
167
|
def read_basic_metadata_from_file(
|
|
128
|
-
file:
|
|
168
|
+
file: ResourcePathExpression, hdrnum: int, can_raise: bool = True
|
|
129
169
|
) -> MutableMapping[str, Any] | None:
|
|
130
170
|
"""Read a raw header from a file, merging if necessary.
|
|
131
171
|
|
|
132
172
|
Parameters
|
|
133
173
|
----------
|
|
134
|
-
file : `str`
|
|
174
|
+
file : `str` or `lsst.resources.ResourcePathExpression`
|
|
135
175
|
Name of file to read. Can be FITS, YAML or JSON. YAML or JSON must be
|
|
136
176
|
a simple top-level dict.
|
|
137
177
|
hdrnum : `int`
|
|
@@ -151,10 +191,11 @@ def read_basic_metadata_from_file(
|
|
|
151
191
|
The header as a dict. Can be `None` if there was a problem reading
|
|
152
192
|
the file.
|
|
153
193
|
"""
|
|
154
|
-
|
|
194
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
195
|
+
if uri.getExtension() in (".yaml", ".json"):
|
|
155
196
|
try:
|
|
156
197
|
md = read_test_file(
|
|
157
|
-
|
|
198
|
+
uri,
|
|
158
199
|
)
|
|
159
200
|
except Exception as e:
|
|
160
201
|
if not can_raise:
|
|
@@ -165,7 +206,7 @@ def read_basic_metadata_from_file(
|
|
|
165
206
|
# YAML can't have HDUs so skip merging below
|
|
166
207
|
hdrnum = 0
|
|
167
208
|
else:
|
|
168
|
-
md = _read_fits_metadata(
|
|
209
|
+
md = _read_fits_metadata(uri, 0, can_raise=can_raise)
|
|
169
210
|
if md is None:
|
|
170
211
|
log.warning("Unable to open file %s", file)
|
|
171
212
|
return None
|
|
@@ -174,19 +215,19 @@ def read_basic_metadata_from_file(
|
|
|
174
215
|
hdrnum = 1
|
|
175
216
|
if hdrnum > 0:
|
|
176
217
|
# Allow this to fail
|
|
177
|
-
mdn = _read_fits_metadata(
|
|
218
|
+
mdn = _read_fits_metadata(uri, int(hdrnum), can_raise=False)
|
|
178
219
|
# Astropy does not allow append mode since it does not
|
|
179
220
|
# convert lists to multiple cards. Overwrite for now
|
|
180
221
|
if mdn is not None:
|
|
181
222
|
md = merge_headers([md, mdn], mode="overwrite")
|
|
182
223
|
else:
|
|
183
|
-
log.warning("HDU %d was not found in file %s. Ignoring request.", hdrnum,
|
|
224
|
+
log.warning("HDU %d was not found in file %s. Ignoring request.", hdrnum, uri)
|
|
184
225
|
|
|
185
226
|
return md
|
|
186
227
|
|
|
187
228
|
|
|
188
229
|
def read_file_info(
|
|
189
|
-
file:
|
|
230
|
+
file: ResourcePathExpression,
|
|
190
231
|
hdrnum: int,
|
|
191
232
|
print_trace: bool | None = None,
|
|
192
233
|
content_mode: str = "translated",
|
|
@@ -232,9 +273,10 @@ def read_file_info(
|
|
|
232
273
|
if content_type not in ("native", "simple", "json"):
|
|
233
274
|
raise ValueError(f"Unrecognized content type request {content_type}")
|
|
234
275
|
|
|
276
|
+
uri = ResourcePath(file, forceAbsolute=False)
|
|
235
277
|
try:
|
|
236
278
|
# Calculate the JSON from the file
|
|
237
|
-
md = read_basic_metadata_from_file(
|
|
279
|
+
md = read_basic_metadata_from_file(uri, hdrnum, can_raise=True if print_trace is None else False)
|
|
238
280
|
if md is None:
|
|
239
281
|
return None
|
|
240
282
|
if content_mode == "metadata":
|
|
@@ -249,7 +291,7 @@ def read_file_info(
|
|
|
249
291
|
json_str = json.dumps(dict(md))
|
|
250
292
|
return json_str
|
|
251
293
|
return md
|
|
252
|
-
obs_info = ObservationInfo(md, pedantic=True, filename=
|
|
294
|
+
obs_info = ObservationInfo(md, pedantic=True, filename=str(uri))
|
|
253
295
|
if content_type == "native":
|
|
254
296
|
return obs_info
|
|
255
297
|
simple = obs_info.to_simple()
|