Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

# This file is part of astro_metadata_translator. 

# 

# Developed for the LSST Data Management System. 

# This product includes software developed by the LSST Project 

# (http://www.lsst.org). 

# See the LICENSE file at the top-level directory of this distribution 

# for details of code ownership. 

# 

# Use of this source code is governed by a 3-clause BSD-style 

# license that can be found in the LICENSE file. 

 

"""Implementation of the ``translate_header.py`` script. 

 

Read file metadata from the specified files and report the translated content. 

""" 

 

__all__ = ("main", "process_files") 

 

import argparse 

 

import os 

import re 

import sys 

import traceback 

import importlib 

import yaml 

from astro_metadata_translator import ObservationInfo, merge_headers 

from astro_metadata_translator.tests import read_test_file 

 

# Prefer afw over Astropy 

try: 

from lsst.afw.fits import readMetadata 

import lsst.daf.base # noqa: F401 need PropertyBase for readMetadata 

 

def read_metadata(file, hdu): 

try: 

return readMetadata(file, hdu=hdu) 

except lsst.afw.fits.FitsError: 

return None 

 

except ImportError: 

from astropy.io import fits 

 

def read_metadata(file, hdu): 

fits_file = fits.open(file) 

try: 

header = fits_file[hdu].header 

except IndexError: 

header = None 

return header 

 

 

def build_argparser(): 

"""Construct an argument parser for the ``translate_header.py`` script. 

 

Returns 

------- 

argparser : `argparse.ArgumentParser` 

The argument parser that defines the ``translate_header.py`` 

command-line interface. 

""" 

 

parser = argparse.ArgumentParser(description="Summarize headers from astronomical data files") 

parser.add_argument("files", metavar="file", type=str, nargs="+", 

help="File(s) from which headers will be parsed." 

" If a directory is given it will be scanned for files matching the regular" 

" expression defined in --regex.") 

parser.add_argument("-q", "--quiet", action="store_true", 

help="Do not report the translation content from each header") 

parser.add_argument("-d", "--dumphdr", action="store_true", 

help="Dump the header in YAML format to standard output rather than translating it") 

parser.add_argument("--traceback", action="store_true", 

help="Give detailed trace back when any errors encountered") 

parser.add_argument("-n", "--hdrnum", default=1, 

help="HDU number to read. If the HDU can not be found, a warning is issued but " 

"translation is attempted using the primary header. " 

"The primary header is always read and merged with this header.") 

 

re_default = r"\.fit[s]?\b" 

parser.add_argument("-r", "--regex", default=re_default, 

help="When looking in a directory, regular expression to use to determine whether" 

f" a file should be examined. Default: '{re_default}'") 

 

parser.add_argument("-p", "--packages", action="append", type=str, 

help="Python packages to import to register additional translators") 

 

return parser 

 

 

def read_file(file, hdrnum, dumphdr, quiet, print_trace, 

outstream=sys.stdout, errstream=sys.stderr): 

"""Read the specified file and process it. 

 

Parameters 

---------- 

file : `str` 

The file from which the header is to be read. 

hdrnum : `int` 

The HDU number to read. The primary header is always read and 

merged with the header from this HDU. 

dumphdr : `bool` 

If `True` dump the merged header to standard output rather than 

translating it. 

quiet : `bool` 

If `True` do not report the translated values for the file. 

print_trace : `bool` 

If there is an error reading the file and this parameter is `True`, 

a full traceback of the exception will be reported. If `False` prints 

a one line summary of the error condition. 

outstream : `io.StringIO`, optional 

Output stream to use for standard messages. Defaults to `sys.stdout`. 

errstream : `io.StringIO`, optional 

Stream to send messages that would normally be sent to standard 

error. Defaults to `sys.stderr`. 

 

Returns 

------- 

success : `bool` 

`True` if the file was handled successfully, `False` if the file 

could not be processed. 

""" 

print(f"Analyzing {file}...", file=errstream) 

try: 

if file.endswith(".yaml"): 

md = read_test_file(file,) 

if hdrnum != 0: 

# YAML can't have HDUs 

hdrnum = 0 

else: 

md = read_metadata(file, 0) 

if md is None: 

print(f"Unable to open file {file}", file=errstream) 

return False 

if hdrnum != 0: 

mdn = read_metadata(file, int(hdrnum)) 

# Astropy does not allow append mode since it does not 

# convert lists to multiple cards. Overwrite for now 

if mdn is not None: 

md = merge_headers([md, mdn], mode="overwrite") 

else: 

print(f"HDU {hdrnum} was not found. Ignoring request.", file=errstream) 

 

if dumphdr: 

# The header should be written out in the insertion order 

print(yaml.dump(md, sort_keys=False), file=outstream) 

return True 

obs_info = ObservationInfo(md, pedantic=True, filename=file) 

if not quiet: 

print(f"{obs_info}", file=outstream) 

except Exception as e: 

if print_trace: 

traceback.print_exc(file=outstream) 

else: 

print(repr(e), file=outstream) 

return False 

return True 

 

 

def process_files(files, regex, hdrnum, dumphdr, quiet, print_trace, 

outstream=sys.stdout, errstream=sys.stderr): 

"""Read and translate metadata from the specified files. 

 

Parameters 

---------- 

files : iterable of `str` 

The files or directories from which the headers are to be read. 

regex : `str` 

Regular expression string used to filter files when a directory is 

scanned. 

hdrnum : `int` 

The HDU number to read. The primary header is always read and 

merged with the header from this HDU. 

dumphdr : `bool` 

If `True` dump the merged header to standard output rather than 

translating it. 

quiet : `bool` 

If `True` do not report the translated values for the file. 

print_trace : `bool` 

If there is an error reading the file and this parameter is `True`, 

a full traceback of the exception will be reported. If `False` prints 

a one line summary of the error condition. 

outstream : `io.StringIO`, optional 

Output stream to use for standard messages. Defaults to `sys.stdout`. 

errstream : `io.StringIO`, optional 

Stream to send messages that would normally be sent to standard 

error. Defaults to `sys.stderr`. 

 

Returns 

------- 

okay : `list` of `str` 

All the files that were processed successfully. 

failed : `list` of `str` 

All the files that could not be processed. 

""" 

file_regex = re.compile(regex) 

failed = [] 

okay = [] 

for file in files: 

if os.path.isdir(file): 

for root, dirs, files in os.walk(file): 

for name in files: 

path = os.path.join(root, name) 

if os.path.isfile(path) and file_regex.search(name): 

isok = read_file(path, hdrnum, dumphdr, quiet, print_trace, outstream, errstream) 

if isok: 

okay.append(path) 

else: 

failed.append(path) 

else: 

isok = read_file(file, hdrnum, dumphdr, quiet, print_trace, outstream, errstream) 

if isok: 

okay.append(file) 

else: 

failed.append(file) 

 

return okay, failed 

 

 

def main(): 

"""Read metadata from the supplied files and translate the content to 

standard form. 

 

Returns 

------- 

status : `int` 

Exit status to be passed to `sys.exit()`. 0 if any of the files 

could be translated. 1 otherwise. 

""" 

args = build_argparser().parse_args() 

 

# Process import requests 

if args.packages: 

for m in args.packages: 

importlib.import_module(m) 

 

# Main loop over files 

okay, failed = process_files(args.files, args.regex, args.hdrnum, 

args.dumphdr, args.quiet, args.traceback) 

 

if failed: 

print("Files with failed translations:", file=sys.stderr) 

for f in failed: 

print(f"\t{f}", file=sys.stderr) 

 

if okay: 

# Good status if anything was returned in okay 

return 0 

else: 

return 1