Skip to content

Commit

Permalink
Make get_first nullable
Browse files Browse the repository at this point in the history
  • Loading branch information
lucc committed Dec 20, 2024
1 parent 2e3bd47 commit 3c07f98
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 18 deletions.
32 changes: 23 additions & 9 deletions khard/contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,19 @@ def __init__(self, vcard: vobject.base.Component,
def __str__(self) -> str:
return self.formatted_name

def get_first(self, property: str, default: str = "") -> str:
@overload
def get_first(self, property: Literal["n"]) -> Optional[vobject.vcard.Name]: ...
@overload
def get_first(self, property: Literal["adr"]) -> Optional[vobject.vcard.Address]: ...
@overload
def get_first(self, property: str) -> Optional[str]: ...
def get_first(self, property: str) -> Union[None, str, vobject.vcard.Name,
vobject.vcard.Address]:
"""Get a property from the underlying vCard.
This method should only be called for properties with cardinality \\*1
(zero or one). Otherwise only the first value will be returned. If the
property is not present a default will be returned.
property is not present None will be retuned.
The type annotation for the return value is str but this is not
enforced so it is up to the caller to make sure to only call this
Expand All @@ -132,7 +139,7 @@ def get_first(self, property: str, default: str = "") -> str:
try:
return getattr(self.vcard, property).value
except AttributeError:
return default
return None

def _get_multi_property(self, name: str) -> list:
"""Get a vCard property that can exist more than once.
Expand Down Expand Up @@ -259,7 +266,7 @@ def _get_types_for_vcard_object(self, object: vobject.base.ContentLine,
return [default_type]

@property
def version(self) -> str:
def version(self) -> Optional[str]:
return self.get_first("version")

@version.setter
Expand All @@ -274,7 +281,7 @@ def version(self, value: str) -> None:
version.value = convert_to_vcard("version", value, ObjectType.str)

@property
def uid(self) -> str:
def uid(self) -> Optional[str]:
return self.get_first("uid")

@uid.setter
Expand Down Expand Up @@ -470,7 +477,7 @@ def _prepare_birthday_value(self, date: Date) -> tuple[Optional[str],

@property
def kind(self) -> str:
return self.get_first(self._kind_attribute_name().lower(), self._default_kind)
return self.get_first(self._kind_attribute_name().lower()) or self._default_kind

@kind.setter
def kind(self, value: str) -> None:
Expand All @@ -487,10 +494,15 @@ def _kind_attribute_name(self) -> str:

@property
def formatted_name(self) -> str:
return self.get_first("fn")
fn = self.get_first("fn")
if fn:
return fn
self.formatted_name = ""
return self.get_first("fn") or ""

@formatted_name.setter
def formatted_name(self, value: str) -> None:
# TODO cardinality 1*
"""Set the FN field to the new value.
All previously existing FN fields are deleted. Version 4 of the specs
Expand Down Expand Up @@ -1303,9 +1315,11 @@ def to_yaml(self) -> str:
"Note": self.notes,
"Webpage": self.webpages,
"Anniversary":
helpers.yaml_anniversary(self.anniversary, self.version),
helpers.yaml_anniversary(self.anniversary, self.version or
self._default_version),
"Birthday":
helpers.yaml_anniversary(self.birthday, self.version),
helpers.yaml_anniversary(self.birthday, self.version or
self._default_version),
"Address": helpers.yaml_addresses(
self.post_addresses, ["Box", "Extended", "Street", "Code",
"City", "Region", "Country"], defaults=["home"])
Expand Down
6 changes: 3 additions & 3 deletions khard/khard.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ def list_contacts(vcard_list: list[Contact], fields: Iterable[str] = (),
row.append(formatter.get_special_field(vcard, field))
elif field == 'uid':
if parsable:
row.append(vcard.uid)
elif abook_collection.get_short_uid(vcard.uid):
row.append(abook_collection.get_short_uid(vcard.uid))
row.append(vcard.uid or "")
elif uid := abook_collection.get_short_uid(vcard.uid or ""):
row.append(uid)
else:
row.append("")
else:
Expand Down
4 changes: 0 additions & 4 deletions test/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,7 @@ def test_and_queries_match_after_sorting(self):


class TestFieldQuery(unittest.TestCase):
@unittest.expectedFailure
def test_empty_field_values_match_if_the_field_is_present(self):
# This test currently fails because the Contact class has all
# attributes set because they are properties. So the test in the query
# class if an attribute is present never fails.
uid = "Some Test Uid"
vcard1 = TestContact(uid=uid)
vcard2 = TestContact()
Expand Down
3 changes: 1 addition & 2 deletions test/test_vcard_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,7 @@ def test_get_only_the_first_property(self):

def test_returns_the_default(self):
wrapper = TestVCardWrapper()
self.assertEqual(wrapper.get_first("title"), "")
self.assertEqual(wrapper.get_first("title", "foo"), "foo")
self.assertIsNone(wrapper.get_first("title"))

def test_can_return_any_value_contradicting_type_annotation(self):
"""This is discouraged!"""
Expand Down

0 comments on commit 3c07f98

Please sign in to comment.