Skip to content

Commit 6958f88

Browse files
authored
Merge pull request #17969 from yuyichao/svgmeta
FIX: Honor `'Date': None` in metadata
2 parents 37cb671 + fffb962 commit 6958f88

File tree

2 files changed

+80
-5
lines changed

2 files changed

+80
-5
lines changed

lib/matplotlib/backends/backend_svg.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,8 @@ def _write_metadata(self, metadata):
360360
'Expected str, date, datetime, or iterable '
361361
'of the same, not {!r}.'.format(type(date)))
362362
metadata['Date'] = '/'.join(dates)
363-
else:
363+
elif 'Date' not in metadata:
364+
# Do not add `Date` if the user explicitly set `Date` to `None`
364365
# Get source date from SOURCE_DATE_EPOCH, if set.
365366
# See https://reproducible-builds.org/specs/source-date-epoch/
366367
date = os.getenv("SOURCE_DATE_EPOCH")
@@ -370,23 +371,30 @@ def _write_metadata(self, metadata):
370371
else:
371372
metadata['Date'] = datetime.datetime.today().isoformat()
372373

373-
mid = writer.start('metadata')
374-
writer.start('rdf:RDF', attrib={
374+
mid = None
375+
def ensure_metadata(mid):
376+
if mid is not None:
377+
return mid
378+
mid = writer.start('metadata')
379+
writer.start('rdf:RDF', attrib={
375380
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
376381
'xmlns:cc': "http://creativecommons.org/ns#",
377382
'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
378383
})
379-
writer.start('cc:Work')
384+
writer.start('cc:Work')
385+
return mid
380386

381387
uri = metadata.pop('Type', None)
382388
if uri is not None:
389+
mid = ensure_metadata(mid)
383390
writer.element('dc:type', attrib={'rdf:resource': uri})
384391

385392
# Single value only.
386393
for key in ['title', 'coverage', 'date', 'description', 'format',
387394
'identifier', 'language', 'relation', 'source']:
388395
info = metadata.pop(key.title(), None)
389396
if info is not None:
397+
mid = ensure_metadata(mid)
390398
writer.element(f'dc:{key}', text=info)
391399

392400
# Multiple Agent values.
@@ -398,6 +406,7 @@ def _write_metadata(self, metadata):
398406
if isinstance(agents, str):
399407
agents = [agents]
400408

409+
mid = ensure_metadata(mid)
401410
writer.start(f'dc:{key}')
402411
for agent in agents:
403412
writer.start('cc:Agent')
@@ -411,14 +420,16 @@ def _write_metadata(self, metadata):
411420
if isinstance(keywords, str):
412421
keywords = [keywords]
413422

423+
mid = ensure_metadata(mid)
414424
writer.start('dc:subject')
415425
writer.start('rdf:Bag')
416426
for keyword in keywords:
417427
writer.element('rdf:li', text=keyword)
418428
writer.end('rdf:Bag')
419429
writer.end('dc:subject')
420430

421-
writer.close(mid)
431+
if mid is not None:
432+
writer.close(mid)
422433

423434
if metadata:
424435
raise ValueError('Unknown metadata key(s) passed to SVG writer: ' +

lib/matplotlib/tests/test_backend_svg.py

+64
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,70 @@ def test_svg_default_metadata(monkeypatch):
371371
# Type
372372
assert 'StillImage' in buf
373373

374+
# Now make sure all the default metadata can be cleared.
375+
with BytesIO() as fd:
376+
fig.savefig(fd, format='svg', metadata={'Date': None, 'Creator': None,
377+
'Format': None, 'Type': None})
378+
buf = fd.getvalue().decode()
379+
380+
# Creator
381+
assert mpl.__version__ not in buf
382+
# Date
383+
assert '1970-08-16' not in buf
384+
# Format
385+
assert 'image/svg+xml' not in buf
386+
# Type
387+
assert 'StillImage' not in buf
388+
389+
390+
def test_svg_clear_default_metadata(monkeypatch):
391+
# Makes sure that setting a default metadata to `None`
392+
# removes the corresponding tag from the metadata.
393+
monkeypatch.setenv('SOURCE_DATE_EPOCH', '19680801')
394+
395+
metadata_contains = {'creator': mpl.__version__, 'date': '1970-08-16',
396+
'format': 'image/svg+xml', 'type': 'StillImage'}
397+
398+
SVGNS = '{http://www.w3.org/2000/svg}'
399+
RDFNS = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}'
400+
CCNS = '{http://creativecommons.org/ns#}'
401+
DCNS = '{http://purl.org/dc/elements/1.1/}'
402+
403+
fig, ax = plt.subplots()
404+
for name in metadata_contains:
405+
with BytesIO() as fd:
406+
fig.savefig(fd, format='svg', metadata={name.title(): None})
407+
buf = fd.getvalue().decode()
408+
409+
root = xml.etree.ElementTree.fromstring(buf)
410+
work, = root.findall(f'./{SVGNS}metadata/{RDFNS}RDF/{CCNS}Work')
411+
for key in metadata_contains:
412+
data = work.findall(f'./{DCNS}{key}')
413+
if key == name:
414+
# The one we cleared is not there
415+
assert not data
416+
continue
417+
# Everything else should be there
418+
data, = data
419+
xmlstr = xml.etree.ElementTree.tostring(data, encoding="unicode")
420+
assert metadata_contains[key] in xmlstr
421+
422+
423+
def test_svg_clear_all_metadata():
424+
# Makes sure that setting all default metadata to `None`
425+
# removes the metadata tag from the output.
426+
427+
fig, ax = plt.subplots()
428+
with BytesIO() as fd:
429+
fig.savefig(fd, format='svg', metadata={'Date': None, 'Creator': None,
430+
'Format': None, 'Type': None})
431+
buf = fd.getvalue().decode()
432+
433+
SVGNS = '{http://www.w3.org/2000/svg}'
434+
435+
root = xml.etree.ElementTree.fromstring(buf)
436+
assert not root.findall(f'./{SVGNS}metadata')
437+
374438

375439
def test_svg_metadata():
376440
single_value = ['Coverage', 'Identifier', 'Language', 'Relation', 'Source',

0 commit comments

Comments
 (0)