3939
4040logger = logging .getLogger (__name__ )
4141T = TypeVar ("T" )
42+ LabeledStrs = list [Union [str , dict [str , str ]]]
4243
4344
4445@overload
@@ -132,17 +133,15 @@ def get_first(self, property: str) -> Union[None, str, vobject.vcard.Name,
132133 str.
133134
134135 :param property: the field value to get
135- :param default: the value to return if the vCard does not have this
136- property
137- :returns: the property value or the default
136+ :returns: the property value or None
138137 """
139138 try :
140139 return getattr (self .vcard , property ).value
141140 except AttributeError :
142141 return None
143142
144- def _get_multi_property (self , name : str ) -> list :
145- """Get a vCard property that can exist more than once .
143+ def get_all (self , name : str ) -> list :
144+ """Get all values of the given vCard property .
146145
147146 It does not matter what the individual vcard properties store as their
148147 value. This function returns them untouched inside an aggregating
@@ -154,14 +153,12 @@ def _get_multi_property(self, name: str) -> list:
154153 :param name: the name of the property (should be UPPER case)
155154 :returns: the values from all occurrences of the named property
156155 """
157- values = []
158- for child in self .vcard .getChildren ():
159- if child .name == name :
160- ablabel = self ._get_ablabel (child )
161- if ablabel :
162- values .append ({ablabel : child .value })
163- else :
164- values .append (child .value )
156+ try :
157+ values = getattr (self .vcard , f"{ name .lower ()} _list" )
158+ except AttributeError :
159+ return []
160+ values = [{label : item .value } if (label := self ._get_ablabel (item ))
161+ else item .value for item in values ]
165162 return sorted (values , key = multi_property_key )
166163
167164 def _delete_vcard_object (self , name : str ) -> None :
@@ -585,12 +582,16 @@ def get_last_name_first_name(self) -> str:
585582 return self .formatted_name
586583
587584 @property
588- def first_name (self ) -> str :
589- return list_to_string (self ._get_first_names (), " " )
585+ def first_name (self ) -> Optional [str ]:
586+ if parts := self ._get_first_names ():
587+ return list_to_string (parts , " " )
588+ return None
590589
591590 @property
592- def last_name (self ) -> str :
593- return list_to_string (self ._get_last_names (), " " )
591+ def last_name (self ) -> Optional [str ]:
592+ if parts := self ._get_last_names ():
593+ return list_to_string (parts , " " )
594+ return None
594595
595596 def _add_name (self , prefix : StrList , first_name : StrList ,
596597 additional_name : StrList , last_name : StrList ,
@@ -617,7 +618,7 @@ def organisations(self) -> list[Union[list[str], dict[str, list[str]]]]:
617618 """
618619 :returns: list of organisations, sorted alphabetically
619620 """
620- return self ._get_multi_property ( "ORG " )
621+ return self .get_all ( "org " )
621622
622623 def _add_organisation (self , organisation : StrList , label : Optional [str ] = None ) -> None :
623624 """Add one ORG entry to the underlying vcard
@@ -640,48 +641,47 @@ def _add_organisation(self, organisation: StrList, label: Optional[str] = None)
640641 showas_obj .value = "COMPANY"
641642
642643 @property
643- def titles (self ) -> list [ Union [ str , dict [ str , str ]]] :
644- return self ._get_multi_property ( "TITLE " )
644+ def titles (self ) -> LabeledStrs :
645+ return self .get_all ( "title " )
645646
646647 def _add_title (self , title : str , label : Optional [str ] = None ) -> None :
647648 self ._add_labelled_property ("title" , title , label , True )
648649
649650 @property
650- def roles (self ) -> list [ Union [ str , dict [ str , str ]]] :
651- return self ._get_multi_property ( "ROLE " )
651+ def roles (self ) -> LabeledStrs :
652+ return self .get_all ( "role " )
652653
653654 def _add_role (self , role : str , label : Optional [str ] = None ) -> None :
654655 self ._add_labelled_property ("role" , role , label , True )
655656
656657 @property
657- def nicknames (self ) -> list [ Union [ str , dict [ str , str ]]] :
658- return self ._get_multi_property ( "NICKNAME " )
658+ def nicknames (self ) -> LabeledStrs :
659+ return self .get_all ( "nickname " )
659660
660661 def _add_nickname (self , nickname : str , label : Optional [str ] = None ) -> None :
661662 self ._add_labelled_property ("nickname" , nickname , label , True )
662663
663664 @property
664- def notes (self ) -> list [ Union [ str , dict [ str , str ]]] :
665- return self ._get_multi_property ( "NOTE " )
665+ def notes (self ) -> LabeledStrs :
666+ return self .get_all ( "note " )
666667
667668 def _add_note (self , note : str , label : Optional [str ] = None ) -> None :
668669 self ._add_labelled_property ("note" , note , label , True )
669670
670671 @property
671- def webpages (self ) -> list [ Union [ str , dict [ str , str ]]] :
672- return self ._get_multi_property ( "URL " )
672+ def webpages (self ) -> LabeledStrs :
673+ return self .get_all ( "url " )
673674
674675 def _add_webpage (self , webpage : str , label : Optional [str ] = None ) -> None :
675676 self ._add_labelled_property ("url" , webpage , label , True )
676677
677678 @property
678679 def categories (self ) -> Union [list [str ], list [list [str ]]]:
679- category_list = []
680- for child in self .vcard .getChildren ():
681- if child .name == "CATEGORIES" :
682- value = child .value
683- category_list .append (
684- value if isinstance (value , list ) else [value ])
680+ category_list = self .get_all ("categories" )
681+ if not category_list :
682+ return category_list
683+ category_list = [value if isinstance (value , list ) else [value ] for
684+ value in category_list ]
685685 if len (category_list ) == 1 :
686686 return category_list [0 ]
687687 return sorted (category_list )
@@ -766,13 +766,16 @@ def emails(self) -> dict[str, list[str]]:
766766 :returns: dict of type and email address list
767767 """
768768 email_dict : dict [str , list [str ]] = {}
769- for child in self .vcard .getChildren ():
770- if child .name == "EMAIL" :
771- type = list_to_string (
772- self ._get_types_for_vcard_object (child , "internet" ), ", " )
773- if type not in email_dict :
774- email_dict [type ] = []
775- email_dict [type ].append (child .value )
769+ try :
770+ emails = self .vcard .email_list
771+ except AttributeError :
772+ return {}
773+ for child in emails :
774+ type = list_to_string (
775+ self ._get_types_for_vcard_object (child , "internet" ), ", " )
776+ if type not in email_dict :
777+ email_dict [type ] = []
778+ email_dict [type ].append (child .value )
776779 # sort email address lists
777780 for email_list in email_dict .values ():
778781 email_list .sort ()
@@ -817,19 +820,22 @@ def post_addresses(self) -> dict[str, list[PostAddress]]:
817820 :returns: dict of type and post address list
818821 """
819822 post_adr_dict : dict [str , list [PostAddress ]] = {}
820- for child in self .vcard .getChildren ():
821- if child .name == "ADR" :
822- type = list_to_string (self ._get_types_for_vcard_object (
823- child , "home" ), ", " )
824- if type not in post_adr_dict :
825- post_adr_dict [type ] = []
826- post_adr_dict [type ].append ({"box" : child .value .box ,
827- "extended" : child .value .extended ,
828- "street" : child .value .street ,
829- "code" : child .value .code ,
830- "city" : child .value .city ,
831- "region" : child .value .region ,
832- "country" : child .value .country })
823+ try :
824+ addresses = self .vcard .adr_list
825+ except AttributeError :
826+ return {}
827+ for child in addresses :
828+ type = list_to_string (self ._get_types_for_vcard_object (
829+ child , "home" ), ", " )
830+ if type not in post_adr_dict :
831+ post_adr_dict [type ] = []
832+ post_adr_dict [type ].append ({"box" : child .value .box ,
833+ "extended" : child .value .extended ,
834+ "street" : child .value .street ,
835+ "code" : child .value .code ,
836+ "city" : child .value .city ,
837+ "region" : child .value .region ,
838+ "country" : child .value .country })
833839 # sort post address lists
834840 for post_adr_list in post_adr_dict .values ():
835841 post_adr_list .sort (key = lambda x : (
@@ -942,9 +948,9 @@ def __init__(self, vcard: vobject.base.Component,
942948 # getters and setters
943949 #####################
944950
945- def _get_private_objects (self ) -> dict [str , list [ Union [ str , dict [ str , str ]]] ]:
951+ def _get_private_objects (self ) -> dict [str , LabeledStrs ]:
946952 supported = [x .lower () for x in self .supported_private_objects ]
947- private_objects : dict [str , list [ Union [ str , dict [ str , str ]]] ] = {}
953+ private_objects : dict [str , LabeledStrs ] = {}
948954 for child in self .vcard .getChildren ():
949955 lower = child .name .lower ()
950956 if lower .startswith ("x-" ) and lower [2 :] in supported :
0 commit comments