1+ # PyADIF-File (c) 2023-2025 by Andreas Schawo is licensed under CC BY-SA 4.0.
2+ # To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
3+
14"""Convert ADIF ADI content to dictionary and vice versa"""
25
36import re
@@ -32,7 +35,7 @@ class IllegalDataTypeException(Exception):
3235REGEX_PARAM = re .compile (r'[a-zA-Z][a-zA-Z_0-9]*' )
3336
3437
35- def unpack (data : str ) -> dict :
38+ def unpack (data : str ) -> dict [ str , str ] :
3639 """Unpack header or record part to dictionary
3740 The parameters are converted to uppercase"""
3841
@@ -77,7 +80,7 @@ def unpack(data: str) -> dict:
7780 return unpacked
7881
7982
80- def loadi (adi : str , skip : int = 0 ) -> Iterator [dict ]:
83+ def loadi (adi : str , skip : int = 0 ) -> Iterator [dict [ str , str ] ]:
8184 """Turn ADI formated string to header/records as an iterator over dict
8285 The skip option is useful if you want to watch a file for new records only. This saves processing time.
8386
@@ -86,30 +89,27 @@ def loadi(adi: str, skip: int = 0) -> Iterator[dict]:
8689 :return: an iterator of records (first record is the header even if not available)
8790 """
8891
89- record_data = adi
90- if not adi .startswith ('<' ): # If a header is available
91- hr_list = re .split (r'<[eE][oO][hH]>' , adi )
92- if len (hr_list ) > 2 :
93- raise TooMuchHeadersException ()
94-
92+ hr_list = re .split (r'<[eE][oO][hH]>' , adi )
93+ if len (hr_list ) == 1 : # Header is missing
94+ yield {}
95+ record_data = hr_list [0 ]
96+ elif len (hr_list ) > 2 : # More than one header
97+ raise TooMuchHeadersException ()
98+ else : # One header and the records
9599 yield unpack (hr_list [0 ])
96100 record_data = hr_list [1 ]
97- else : # Empty record for missing header
98- yield {}
99101
100- i = 0
101- for rec in re .finditer (r'(.*?)<[eE][oO][rR]>' , record_data , re .S ):
102+ for i , rec in enumerate (re .finditer (r'(.*?)<[eE][oO][rR]>' , record_data , re .S )):
102103 if i >= skip :
103104 yield unpack (rec .groups ()[0 ])
104- i += 1
105105
106106
107107def loads (adi : str , skip : int = 0 ) -> dict :
108108 """Turn ADI formated string to dictionary
109109 The parameters are converted to uppercase
110110
111111 {
112- 'HEADER': None ,
112+ 'HEADER': {} ,
113113 'RECORDS': [list of records]
114114 }
115115
@@ -121,7 +121,7 @@ def loads(adi: str, skip: int = 0) -> dict:
121121 :return: the ADI as a dict
122122 """
123123
124- doc = {'HEADER' : None ,
124+ doc = {'HEADER' : {} ,
125125 'RECORDS' : []
126126 }
127127
@@ -141,7 +141,7 @@ def load(file_name: str, skip: int = 0, encoding=None) -> dict:
141141 The parameters are converted to uppercase
142142
143143 {
144- 'HEADER': None ,
144+ 'HEADER': {} ,
145145 'RECORDS': [list of records]
146146 }
147147
@@ -164,7 +164,7 @@ def pack(param: str, value: str, dtype: str = None) -> str:
164164 """Generates ADI tag if value is not empty
165165 Does not generate tags for *_INTL types as required by specification.
166166
167- :param param: the tag parameter (converte to uppercase)
167+ :param param: the tag parameter (converted to uppercase)
168168 :param value: the tag value (or tag definition if param is a USERDEF field)
169169 :param dtype: the optional datatype (mainly used for USERDEFx in header)
170170 :return: <param:length>value
@@ -297,7 +297,6 @@ def dump(file_name: str, data_dict: dict, comment: str = 'ADIF export by ' + __p
297297 first = False
298298 else :
299299 af .write ('\n \n ' if linebreaks else '\n ' )
300-
301300 af .write (chunk )
302301
303302
0 commit comments