diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py
index 97705dd3..06669126 100644
--- a/novelwriter/core/indexdata.py
+++ b/novelwriter/core/indexdata.py
@@ -291,20 +291,20 @@ class IndexHeading:
self._comments["summary"] = str(text)
return
- def setTag(self, tagKey: str) -> None:
+ def setTag(self, tag: str) -> None:
"""Set the tag for references, and make sure it is a string."""
- self._tag = str(tagKey).lower()
+ self._tag = str(tag).lower()
return
- def addReference(self, tagKey: str, refType: str) -> None:
+ def addReference(self, tag: str, keyword: str) -> None:
"""Add a record of a reference tag, and what keyword types it is
associated with.
"""
- if refType in nwKeyWords.VALID_KEYS:
- tagKey = tagKey.lower()
- if tagKey not in self._refs:
- self._refs[tagKey] = set()
- self._refs[tagKey].add(refType)
+ if keyword in nwKeyWords.VALID_KEYS:
+ tag = tag.lower()
+ if tag not in self._refs:
+ self._refs[tag] = set()
+ self._refs[tag].add(keyword)
return
##
@@ -337,18 +337,18 @@ class IndexHeading:
self.setLine(entry.get("line", 0))
self.setCounts(entry.get("counts", [0, 0, 0]))
elif key == "refs":
- for key, types in entry.items():
- if not isinstance(key, str):
- raise ValueError("itemIndex reference key must be a string")
- if not isinstance(types, str):
- raise ValueError("itemIndex reference type must be a string")
- for refType in types.split(","):
- if refType in nwKeyWords.VALID_KEYS:
- self.addReference(key, refType)
+ for tag, value in entry.items():
+ if not isinstance(tag, str):
+ raise ValueError("Heading reference key must be a string")
+ if not isinstance(value, str):
+ raise ValueError("Heading reference value must be a string")
+ for keyword in value.split(","):
+ if keyword in nwKeyWords.VALID_KEYS:
+ self.addReference(tag, keyword)
else:
- raise ValueError("The itemIndex contains an invalid reference type")
+ raise ValueError("Heading reference contains an invalid keyword")
elif key == "summary" or key.startswith("story"):
self._comments[str(key)] = str(entry)
else:
- raise KeyError("Unknown key in itemIndex")
+ raise KeyError("Unknown key in heading entry")
return
diff --git a/tests/reference/coreIndex_LoadSave_tagsIndex.json b/tests/reference/coreIndex_LoadSave_tagsIndex.json
index 4412cd18..99201a13 100644
--- a/tests/reference/coreIndex_LoadSave_tagsIndex.json
+++ b/tests/reference/coreIndex_LoadSave_tagsIndex.json
@@ -6,107 +6,103 @@
},
"novelWriter.itemIndex": {
"7a992350f3eb6": {
- "headings": {
- "T0001": {"level": "H1", "title": "Lorem Ipsum", "line": 1, "tag": "", "cCount": 259, "wCount": 44, "pCount": 5, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H1", "title": "Lorem Ipsum", "line": 1, "tag": "", "counts": [259, 44, 5]}
}
},
"8c58a65414c23": {
- "headings": {
- "T0000": {"level": "H0", "title": "", "line": 0, "tag": "", "cCount": 1058, "wCount": 176, "pCount": 2, "synopsis": ""}
+ "T0000": {
+ "meta": {"level": "H0", "title": "", "line": 0, "tag": "", "counts": [1058, 176, 2]}
}
},
"88d59a277361b": {
- "headings": {
- "T0001": {"level": "H2", "title": "Prologue", "line": 1, "tag": "", "cCount": 605, "wCount": 94, "pCount": 2, "synopsis": "Explanation from the lipsum.com website."}
+ "T0001": {
+ "meta": {"level": "H2", "title": "Prologue", "line": 1, "tag": "", "counts": [605, 94, 2]},
+ "summary": "Explanation from the lipsum.com website."
},
- "notes": {
+ "document": {
"footnotes": ["f9kgf"]
}
},
"db7e733775d4d": {
- "headings": {
- "T0001": {"level": "H1", "title": "Act One", "line": 1, "tag": "", "cCount": 35, "wCount": 6, "pCount": 1, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H1", "title": "Act One", "line": 1, "tag": "", "counts": [35, 6, 1]}
}
},
"fb609cd8319dc": {
- "headings": {
- "T0001": {"level": "H2", "title": "Chapter One", "line": 1, "tag": "", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."}
- },
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0001": {
+ "meta": {"level": "H2", "title": "Chapter One", "line": 1, "tag": "", "counts": [419, 67, 1]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."
}
},
"88243afbe5ed8": {
- "headings": {
- "T0001": {"level": "H3", "title": "Scene One", "line": 1, "tag": "", "cCount": 1197, "wCount": 174, "pCount": 2, "synopsis": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."},
- "T0002": {"level": "H4", "title": "Scene One, Section Two", "line": 13, "tag": "", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H3", "title": "Scene One", "line": 1, "tag": "", "counts": [1197, 174, 2]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."
},
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0002": {
+ "meta": {"level": "H4", "title": "Scene One, Section Two", "line": 13, "tag": "", "counts": [1561, 230, 2]}
}
},
"f96ec11c6a3da": {
- "headings": {
- "T0001": {"level": "H3", "title": "Scene Two", "line": 1, "tag": "", "cCount": 2034, "wCount": 299, "pCount": 3, "synopsis": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."},
- "T0002": {"level": "H4", "title": "Scene Two, Section Two", "line": 15, "tag": "", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H3", "title": "Scene Two", "line": 1, "tag": "", "counts": [2034, 299, 3]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."
},
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0002": {
+ "meta": {"level": "H4", "title": "Scene Two, Section Two", "line": 15, "tag": "", "counts": [2009, 301, 3]}
}
},
"846352075de7d": {
- "headings": {
- "T0001": {"level": "H2", "title": "Why do we use it?", "line": 1, "tag": "", "cCount": 631, "wCount": 109, "pCount": 3, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H2", "title": "Why do we use it?", "line": 1, "tag": "", "counts": [631, 109, 3]}
}
},
"441420a886d82": {
- "headings": {
- "T0001": {"level": "H2", "title": "Chapter Two", "line": 1, "tag": "", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."}
- },
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0001": {
+ "meta": {"level": "H2", "title": "Chapter Two", "line": 1, "tag": "", "counts": [477, 70, 1]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."
}
},
"eb103bc70c90c": {
- "headings": {
- "T0001": {"level": "H3", "title": "Scene Three", "line": 1, "tag": "", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."}
- },
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0001": {
+ "meta": {"level": "H3", "title": "Scene Three", "line": 1, "tag": "", "counts": [3006, 439, 4]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."
}
},
"f8c0562e50f1b": {
- "headings": {
- "T0001": {"level": "H3", "title": "Scene Four", "line": 1, "tag": "", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."}
- },
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0001": {
+ "meta": {"level": "H3", "title": "Scene Four", "line": 1, "tag": "", "counts": [3839, 563, 6]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."
}
},
"47666c91c7ccf": {
- "headings": {
- "T0001": {"level": "H3", "title": "Scene Five", "line": 1, "tag": "", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}
- },
- "references": {
- "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"}
+ "T0001": {
+ "meta": {"level": "H3", "title": "Scene Five", "line": 1, "tag": "", "counts": [3644, 543, 5]},
+ "refs": {"bod": "@pov", "main": "@plot", "europe": "@location"},
+ "summary": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."
}
},
"4c4f28287af27": {
- "headings": {
- "T0001": {"level": "H1", "title": "Nobody Owens", "line": 1, "tag": "bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""}
- },
- "references": {
- "T0001": {"main": "@plot"}
+ "T0001": {
+ "meta": {"level": "H1", "title": "Nobody Owens", "line": 1, "tag": "bod", "counts": [1864, 284, 3]},
+ "refs": {"main": "@plot"}
}
},
"2426c6f0ca922": {
- "headings": {
- "T0001": {"level": "H1", "title": "Main Plot", "line": 1, "tag": "main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H1", "title": "Main Plot", "line": 1, "tag": "main", "counts": [1369, 195, 2]}
}
},
"04468803b92e1": {
- "headings": {
- "T0001": {"level": "H1", "title": "Ancient Europe", "line": 1, "tag": "europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""}
+ "T0001": {
+ "meta": {"level": "H1", "title": "Ancient Europe", "line": 1, "tag": "europe", "counts": [1770, 259, 3]}
}
}
}
diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py
index ea140a44..86e34915 100644
--- a/tests/test_core/test_core_index.py
+++ b/tests/test_core/test_core_index.py
@@ -1198,13 +1198,13 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd):
idxData = itemIndex.packData()
assert idxData[cHandle]["T0001"]["meta"] == {
- "level": "H2", "line": 1, "title": "Chapter One", "tag": "one",
- "counts": (60, 10, 2), "summary": "In the beginning ...",
+ "level": "H2", "line": 1, "title": "Chapter One", "tag": "one", "counts": (60, 10, 2),
}
assert "@pov" in idxData[cHandle]["T0001"]["refs"]["jane"]
assert "@focus" in idxData[cHandle]["T0001"]["refs"]["jane"]
assert "@char" in idxData[cHandle]["T0001"]["refs"]["jane"]
assert "@char" in idxData[cHandle]["T0001"]["refs"]["john"]
+ assert idxData[cHandle]["T0001"]["summary"] == "In the beginning ..."
# Add the other two files
itemIndex.add(nHandle, project.tree[nHandle]) # type: ignore
@@ -1310,112 +1310,3 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd):
# Delete new item
del itemIndex[uHandle] # type: ignore
assert uHandle not in itemIndex
-
- # Unpack Error Handling
- # =====================
-
- # Pack/unpack should restore state
- content = itemIndex.packData()
- itemIndex.clear()
- itemIndex.unpackData(content)
- assert itemIndex.packData() == content
- itemIndex.clear()
-
- # Data must be dictionary
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData("stuff") # type: ignore
- assert str(exc.value) == "itemIndex is not a dict"
-
- # Keys must be valid handles
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({"stuff": "more stuff"})
- assert str(exc.value) == "itemIndex keys must be handles"
-
- # Unknown keys should be skipped
- itemIndex.unpackData({C.hInvalid: {}})
- assert itemIndex._items == {}
-
- # Known keys can be added, even without data
- itemIndex.unpackData({nHandle: {}})
- assert nHandle in itemIndex
- assert itemIndex[nHandle].handle == nHandle # type: ignore
-
- # Title tags must be valid
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({cHandle: {"headings": {"TTTTTTT": {}}}})
- assert str(exc.value) == "The itemIndex contains an invalid title key"
-
- # Reference without a heading should be rejected
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {}, "T0002": {}},
- }
- })
- assert "T0001" in itemIndex[cHandle] # type: ignore
- assert "T0002" not in itemIndex[cHandle] # type: ignore
- itemIndex.clear()
-
- # Tag keys must be strings
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {1234: "@pov"}},
- "notes": {"footnotes": [], "comments": []},
- }
- })
- assert str(exc.value) == "itemIndex reference key must be a string"
-
- # Type must be strings
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {"John": []}},
- "notes": {"footnotes": [], "comments": []},
- }
- })
- assert str(exc.value) == "itemIndex reference type must be a string"
-
- # Types must be valid
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {"John": "@pov,@char,@stuff"}},
- "notes": {"footnotes": [], "comments": []},
- }
- })
- assert str(exc.value) == "The itemIndex contains an invalid reference type"
-
- # Note type must be valid
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {"John": "@pov,@char"}},
- "notes": {"stuff": [], "comments": []},
- }
- })
- assert str(exc.value) == "The notes style is invalid"
-
- # Note keys must be all strings
- with pytest.raises(ValueError) as exc:
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {"John": "@pov,@char"}},
- "notes": {"footnotes": ["fkey", 1], "comments": []},
- }
- })
- assert str(exc.value) == "The notes keys must be a list of strings"
-
- # This should pass
- itemIndex.unpackData({
- cHandle: {
- "headings": {"T0001": {}},
- "references": {"T0001": {"John": "@pov,@char"}},
- "notes": {"footnotes": ["fkey"], "comments": ["ckey"]},
- }
- })
diff --git a/tests/test_core/test_core_indexdata.py b/tests/test_core/test_core_indexdata.py
new file mode 100644
index 00000000..7ca7fb78
--- /dev/null
+++ b/tests/test_core/test_core_indexdata.py
@@ -0,0 +1,172 @@
+"""
+novelWriter – NWIndex Class Tester
+==================================
+
+This file is a part of novelWriter
+Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+from __future__ import annotations
+
+import pytest
+
+from novelwriter.core.indexdata import IndexHeading
+
+
+@pytest.mark.core
+def testCoreIndexData_IndexHeading():
+ """Test the IndexHeading class."""
+ # Defaults
+ head = IndexHeading("T0001")
+ assert repr(head) == ""
+ assert str(head) == ""
+ assert head.key == "T0001"
+ assert head.line == 0
+ assert head.level == "H0"
+ assert head.title == ""
+ assert head.charCount == 0
+ assert head.wordCount == 0
+ assert head.paraCount == 0
+ assert head.synopsis == ""
+ assert head.tag == ""
+ assert head.references == {}
+
+ # Set Level
+ head.setLevel("Stuff") # Invalid
+ assert head.level == "H0"
+ head.setLevel("H1") # Valid
+ assert head.level == "H1"
+
+ # Set Line
+ head.setLine(-1) # Invalid
+ assert head.line == 0
+ head.setLine(42) # Valid
+ assert head.line == 42
+
+ # Set Counts
+ head.setCounts([1, 2]) # Invalid, must be three values
+ assert head.charCount == 0
+ assert head.wordCount == 0
+ assert head.paraCount == 0
+ head.setCounts([42, 4, 2]) # Valid
+ assert head.charCount == 42
+ assert head.wordCount == 4
+ assert head.paraCount == 2
+
+ # Set Summary
+ head.setSynopsis("In the beginning ...")
+ assert head.synopsis == "In the beginning ..."
+
+ # Set Tag
+ head.setTag("Stuff")
+ assert head.tag == "stuff" # Case insensitive
+
+ # Add References
+ head.addReference("Stuff", "@stuff") # Invalid type
+ assert head.references == {}
+ head.addReference("Stuff", "@object") # Valid type
+ assert head.references == {"stuff": {"@object"}}
+
+ # Pack Data
+ assert head.packData() == {
+ "meta": {"level": "H1", "title": "", "line": 42, "tag": "stuff", "counts": (42, 4, 2)},
+ "refs": {"stuff": "@object"},
+ "summary": "In the beginning ...",
+ }
+
+ # Unpack KeyError
+ with pytest.raises(KeyError, match="Unknown key in heading entry"):
+ head.unpackData({"stuff": "more stuff"})
+
+ # Unpack Comments
+ head.unpackData({"summary": "How it started ..."})
+ assert head.synopsis == "How it started ..."
+
+
+@pytest.mark.core
+def testCoreIndexData_IndexHeadingUnpackMeta():
+ """Test IndexHeading class meta unpacking."""
+ # Valid
+ data = {"meta": {
+ "level": "H1", "title": "So it Begins", "line": 1, "tag": "begins", "counts": [95, 18, 1]
+ }}
+ head = IndexHeading("T0001")
+ head.unpackData(data)
+ assert head.level == "H1"
+ assert head.title == "So it Begins"
+ assert head.line == 1
+ assert head.tag == "begins"
+ assert head.charCount == 95
+ assert head.wordCount == 18
+ assert head.paraCount == 1
+
+ # Invalid
+ data = {"meta": {
+ "level": "H9", "title": None, "line": None, "tag": None, "counts": [42]
+ }}
+ head = IndexHeading("T0001")
+ head.unpackData(data)
+ assert head.level == "H0"
+ assert head.title == "None"
+ assert head.line == 0
+ assert head.tag == "None"
+ assert head.charCount == 0
+ assert head.wordCount == 0
+ assert head.paraCount == 0
+
+ # Empty
+ data = {"meta": {}}
+ head = IndexHeading("T0001")
+ head.unpackData(data)
+ assert head.level == "H0"
+ assert head.title == ""
+ assert head.line == 0
+ assert head.tag == ""
+ assert head.charCount == 0
+ assert head.wordCount == 0
+ assert head.paraCount == 0
+
+
+@pytest.mark.core
+def testCoreIndexData_IndexHeadingUnpackRefs():
+ """Test IndexHeading class refs unpacking."""
+ # Valid
+ data = {"refs": {
+ "jane": "@char,@pov", "john": "@char", "earth": "@location", "space": "@mention,@location"
+ }}
+ head = IndexHeading("T0001")
+ head.unpackData(data)
+ assert head.references["jane"] == {"@char", "@pov"}
+ assert head.references["john"] == {"@char"}
+ assert head.references["earth"] == {"@location"}
+ assert head.references["space"] == {"@location", "@mention"}
+
+ # Invalid key
+ data = {"refs": {0: "@char,@pov"}}
+ head = IndexHeading("T0001")
+ with pytest.raises(ValueError, match="Heading reference key must be a string"):
+ head.unpackData(data)
+
+ # Invalid value
+ data = {"refs": {"jane": None}}
+ head = IndexHeading("T0001")
+ with pytest.raises(ValueError, match="Heading reference value must be a string"):
+ head.unpackData(data)
+
+ # Invalid keyword
+ data = {"refs": {"jane": "@char,@pov,@stuff"}}
+ head = IndexHeading("T0001")
+ with pytest.raises(ValueError, match="Heading reference contains an invalid keyword"):
+ head.unpackData(data)