diff --git a/bibtexparser/model.py b/bibtexparser/model.py index 3e93f63..421aef9 100644 --- a/bibtexparser/model.py +++ b/bibtexparser/model.py @@ -378,7 +378,12 @@ def get(self, key: str, default=None) -> Optional[Field]: return self.fields_dict.get(key, default) def __contains__(self, key: str) -> bool: - """Dict-mimicking ``in`` operator.""" + """Dict-mimicking ``in`` operator. + + As in ``__getitem__``, ``ENTRYTYPE`` and ``ID`` are always contained. + """ + if key in ("ENTRYTYPE", "ID"): + return True return key in self.fields_dict def __getitem__(self, key: str) -> Any: @@ -400,8 +405,16 @@ def __setitem__(self, key: str, value: Any): This serves for partial v1.x backwards compatibility, as well as for a shorthand for `set_field`. + + Mirroring ``__getitem__``, the keys ``ENTRYTYPE`` and ``ID`` + set ``entry_type`` and ``key`` instead of a field. """ - self.set_field(Field(key, value)) + if key == "ENTRYTYPE": + self.entry_type = value + elif key == "ID": + self.key = value + else: + self.set_field(Field(key, value)) def __delitem__(self, key: str) -> None: """Dict-mimicking index. diff --git a/tests/test_model.py b/tests/test_model.py index 497fce4..c00929e 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -96,6 +96,30 @@ def test_entry_contains(): entry = Entry("article", "key", [Field("field", "value", 1)], 1, "raw") assert "field" in entry assert "other" not in entry + # ENTRYTYPE and ID are exposed by `__getitem__`, hence always contained + assert "ENTRYTYPE" in entry + assert "ID" in entry + + +def test_entry_setitem_field(): + entry = Entry("article", "key", [Field("field", "value", 1)], 1, "raw") + entry["field"] = "new_value" + entry["other"] = "other_value" + assert entry["field"] == "new_value" + assert entry["other"] == "other_value" + assert [f.key for f in entry.fields] == ["field", "other"] + + +def test_entry_setitem_entrytype_and_id(): + entry = Entry("article", "key", [Field("field", "value", 1)], 1, "raw") + entry["ENTRYTYPE"] = "book" + entry["ID"] = "new_key" + assert entry.entry_type == "book" + assert entry.key == "new_key" + assert entry["ENTRYTYPE"] == "book" + assert entry["ID"] == "new_key" + # No literal fields must be created for these keys + assert [f.key for f in entry.fields] == ["field"] def test_string_equality():