Skip to content

Commit 7f7e355

Browse files
authored
Add explicit support for history in VRT files (#12)
* Add explicit support for history in VRT files * Keep VRT stuff inside guard for no history case * Describe VRT special case in the docstring
1 parent 26c9b1b commit 7f7e355

File tree

1 file changed

+60
-26
lines changed

1 file changed

+60
-26
lines changed

processinghistory/history.py

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@
3030
value being a list of keys of the parents of that file. This dictionary stores
3131
all the ancestry relationships for the whole lineage.
3232
33+
History in VRT files
34+
--------------------
35+
A GDAL VRT file is handled as a somewhat special case. The component files
36+
of the VRT are treated as parents of the VRT (and there can be no other parents),
37+
and the history of those files is read directly from them, rather than being
38+
copied into the VRT. This is handled transparently, so that when history
39+
is read from the VRT, it appears to have all come from there. This allows the
40+
history of the components to be as dynamic as the data itself.
41+
3342
"""
3443
import sys
3544
import os
@@ -54,6 +63,7 @@
5463
PARENTS_BY_KEY = "parentsByKey"
5564
AUTOENVVARSLIST_NAME = "HISTORY_ENVVARS_TO_AUTOINCLUDE"
5665
NO_TIMESTAMP = "UnknownTimestamp"
66+
TIMESTAMP = "timestamp"
5767

5868
# These GDAL drivers are known to have limits on the size of metadata which
5969
# can be stored, and so we need to keep below these, or we lose everything.
@@ -70,9 +80,36 @@ def __init__(self):
7080
self.metadataByKey = {}
7181
self.parentsByKey = {}
7282

83+
def addParentHistory(self, parentfile):
84+
"""
85+
Add history from parent file to self
86+
"""
87+
parentHist = readHistoryFromFile(filename=parentfile)
88+
89+
if parentHist is not None:
90+
key = (os.path.basename(parentfile),
91+
parentHist.metadataByKey[CURRENTFILE_KEY][TIMESTAMP])
92+
93+
# Convert parent's "currentfile" metadata and parentage to normal key entries
94+
self.metadataByKey[key] = parentHist.metadataByKey[CURRENTFILE_KEY]
95+
self.parentsByKey[key] = parentHist.parentsByKey[CURRENTFILE_KEY]
96+
97+
# Remove those from parentHist
98+
parentHist.metadataByKey.pop(CURRENTFILE_KEY)
99+
parentHist.parentsByKey.pop(CURRENTFILE_KEY)
100+
101+
# Copy over all the other ancestor metadata and parentage
102+
self.metadataByKey.update(parentHist.metadataByKey)
103+
self.parentsByKey.update(parentHist.parentsByKey)
104+
else:
105+
key = (os.path.basename(parentfile), NO_TIMESTAMP)
106+
107+
# Add this parent as parent of current file
108+
self.parentsByKey[CURRENTFILE_KEY].append(key)
109+
73110
def toJSON(self):
74111
"""
75-
Return a JSON representation of the given ProcessingHistory
112+
Return a JSON representation of the current ProcessingHistory
76113
"""
77114
d = {
78115
METADATA_BY_KEY: {},
@@ -131,7 +168,7 @@ def makeAutomaticFields():
131168
dictn = {}
132169

133170
# Time stamp formatted as per ISO 8601 standard, including time zone offset
134-
dictn['timestamp'] = time.strftime("%Y-%m-%d %H:%M:%S%z", time.localtime())
171+
dictn[TIMESTAMP] = time.strftime("%Y-%m-%d %H:%M:%S%z", time.localtime())
135172

136173
dictn['login'] = getpass.getuser()
137174

@@ -239,8 +276,6 @@ def writeHistoryToFile(userDict={}, parents=[], *, filename=None, gdalDS=None):
239276
File can be specified as either a filename string or an open GDAL Dataset
240277
241278
"""
242-
procHist = makeProcessingHistory(userDict, parents)
243-
244279
if filename is not None:
245280
ds = gdal.Open(filename, gdal.GA_Update)
246281
else:
@@ -250,6 +285,12 @@ def writeHistoryToFile(userDict={}, parents=[], *, filename=None, gdalDS=None):
250285
raise ProcessingHistoryError("Must supply either filename or gdalDS")
251286

252287
drvrName = ds.GetDriver().ShortName
288+
isVRT = (drvrName == "VRT")
289+
if isVRT and len(parents) > 0:
290+
msg = "History for VRT files should not have parents"
291+
raise ProcessingHistoryError(msg)
292+
293+
procHist = makeProcessingHistory(userDict, parents)
253294

254295
# Convert to JSON
255296
procHistJSON = procHist.toJSON()
@@ -295,28 +336,7 @@ def makeProcessingHistory(userDict, parents):
295336
# Now add history from each parent file
296337
procHist.parentsByKey[CURRENTFILE_KEY] = []
297338
for parentfile in parents:
298-
parentHist = readHistoryFromFile(filename=parentfile)
299-
300-
if parentHist is not None:
301-
key = (os.path.basename(parentfile),
302-
parentHist.metadataByKey[CURRENTFILE_KEY]['timestamp'])
303-
304-
# Convert parent's "currentfile" metadata and parentage to normal key entries
305-
procHist.metadataByKey[key] = parentHist.metadataByKey[CURRENTFILE_KEY]
306-
procHist.parentsByKey[key] = parentHist.parentsByKey[CURRENTFILE_KEY]
307-
308-
# Remove those from parentHist
309-
parentHist.metadataByKey.pop(CURRENTFILE_KEY)
310-
parentHist.parentsByKey.pop(CURRENTFILE_KEY)
311-
312-
# Copy over all the other ancestor metadata and parentage
313-
procHist.metadataByKey.update(parentHist.metadataByKey)
314-
procHist.parentsByKey.update(parentHist.parentsByKey)
315-
else:
316-
key = (os.path.basename(parentfile), NO_TIMESTAMP)
317-
318-
# Add this parent as parent of current file
319-
procHist.parentsByKey[CURRENTFILE_KEY].append(key)
339+
procHist.addParentHistory(parentfile)
320340

321341
return procHist
322342

@@ -342,6 +362,20 @@ def readHistoryFromFile(filename=None, gdalDS=None):
342362

343363
if procHistJSON is not None:
344364
procHist = ProcessingHistory.fromJSON(procHistJSON)
365+
366+
# If this is a VRT, then read the component files as though they were
367+
# parent files
368+
isVRT = (ds.GetDriver().ShortName == "VRT")
369+
if isVRT:
370+
vrtFile = ds.GetDescription()
371+
componentList = [fn for fn in ds.GetFileList() if fn != vrtFile]
372+
for componentFile in componentList:
373+
if not os.path.exists(componentFile):
374+
msg = (f"VRT file '{vrtFile}' missing component " +
375+
f"'{componentFile}'")
376+
raise ProcessingHistoryError(msg)
377+
378+
procHist.addParentHistory(componentFile)
345379
else:
346380
procHist = None
347381

0 commit comments

Comments
 (0)