Skip to content
This repository was archived by the owner on Dec 26, 2025. It is now read-only.

Commit 66ae873

Browse files
committed
Strip tags on loading
1 parent ee581a9 commit 66ae873

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

src/adif_file/adi.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@ class IllegalDataTypeException(Exception):
3535
REGEX_PARAM = re.compile(r'[a-zA-Z][a-zA-Z_0-9]*')
3636

3737

38-
def unpack(data: str) -> dict[str, str]:
38+
def unpack(data: str, strip_tags: bool = True) -> dict[str, str]:
3939
"""Unpack header or record part to dictionary
40-
The parameters are converted to uppercase"""
40+
The parameters are converted to uppercase
41+
:param data: string with multiple ADIF tag and value for a whole record
42+
:param strip_tags: remove any leading or trailing whitespaces in tag names (default: True)
43+
:return: dictionary of ADIF tag and value"""
4144

4245
unpacked = {}
4346

@@ -56,7 +59,7 @@ def unpack(data: str) -> dict[str, str]:
5659
dtype = None
5760
try:
5861
tag_def = tag.split(':')
59-
param = tag_def[0]
62+
param = tag_def[0].strip() if strip_tags else tag_def[0]
6063
length = tag_def[1]
6164
if len(tag_def) == 3:
6265
dtype = tag_def[2]
@@ -80,12 +83,13 @@ def unpack(data: str) -> dict[str, str]:
8083
return unpacked
8184

8285

83-
def loadi(adi: str, skip: int = 0) -> Iterator[dict[str, str]]:
86+
def loadi(adi: str, skip: int = 0, strip_tags: bool = True) -> Iterator[dict[str, str]]:
8487
"""Turn ADI formated string to header/records as an iterator over dict
8588
The skip option is useful if you want to watch a file for new records only. This saves processing time.
8689
8790
:param adi: the ADI data
8891
:param skip: skip first number of records (does not apply for header)
92+
:param strip_tags: remove any leading or trailing whitespaces in tag names (default: True)
8993
:return: an iterator of records (first record is the header even if not available)
9094
"""
9195

@@ -96,15 +100,15 @@ def loadi(adi: str, skip: int = 0) -> Iterator[dict[str, str]]:
96100
elif len(hr_list) > 2: # More than one header
97101
raise TooMuchHeadersException()
98102
else: # One header and the records
99-
yield unpack(hr_list[0])
103+
yield unpack(hr_list[0], strip_tags)
100104
record_data = hr_list[1]
101105

102106
for i, rec in enumerate(re.finditer(r'(.*?)<[eE][oO][rR]>', record_data, re.S)):
103107
if i >= skip:
104-
yield unpack(rec.groups()[0])
108+
yield unpack(rec.groups()[0], strip_tags)
105109

106110

107-
def loads(adi: str, skip: int = 0) -> dict:
111+
def loads(adi: str, skip: int = 0, strip_tags: bool = True) -> dict:
108112
"""Turn ADI formated string to dictionary
109113
The parameters are converted to uppercase
110114
@@ -118,6 +122,7 @@ def loads(adi: str, skip: int = 0) -> dict:
118122
119123
:param adi: the ADI data
120124
:param skip: skip first number of records (does not apply for header)
125+
:param strip_tags: remove any leading or trailing whitespaces in tag names (default: True)
121126
:return: the ADI as a dict
122127
"""
123128

@@ -126,7 +131,7 @@ def loads(adi: str, skip: int = 0) -> dict:
126131
}
127132

128133
first = True
129-
for rec in loadi(adi, skip):
134+
for rec in loadi(adi, skip, strip_tags):
130135
if first:
131136
doc['HEADER'] = rec
132137
first = False
@@ -136,7 +141,7 @@ def loads(adi: str, skip: int = 0) -> dict:
136141
return doc
137142

138143

139-
def load(file_name: str, skip: int = 0, encoding=None) -> dict:
144+
def load(file_name: str, skip: int = 0, encoding=None, strip_tags: bool = True) -> dict:
140145
"""Load ADI formated file to dictionary
141146
The parameters are converted to uppercase
142147
@@ -151,13 +156,14 @@ def load(file_name: str, skip: int = 0, encoding=None) -> dict:
151156
:param file_name: the file name where the ADI data is stored
152157
:param skip: skip first number of records (does not apply for header)
153158
:param encoding: the file encoding
159+
:param strip_tags: remove any leading or trailing whitespaces in tag names (default: True)
154160
:return: the ADI as a dict
155161
"""
156162

157163
with open(file_name, encoding=encoding) as af:
158164
data = af.read()
159165

160-
return loads(data, skip)
166+
return loads(data, skip, strip_tags)
161167

162168

163169
def pack(param: str, value: str, dtype: str = None) -> str:

test/test_loadadi.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# PyADIF-File (c) 2024 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
import os
25
import unittest
36

@@ -39,13 +42,42 @@ def test_15_unpack_userdef(self):
3942
self.assertDictEqual(exp_dict1, adif_file.adi.unpack(adi_hdr1))
4043
self.assertDictEqual(exp_dict2, adif_file.adi.unpack(adi_hdr2))
4144

45+
def test_17_unpack_header_strip(self):
46+
adi_hdr1 = '''ADIF Export by Testprog
47+
<ADIF_VER :5>3.1.4 < PROGRAMID:8>Testprog < PROGRAMVERSION :4>v0.2'''
48+
adi_hdr2 = '''ADIF Export by Testprog
49+
<ADIF_VER :5>3.1.4
50+
< PROGRAMID:8>Testprog
51+
< PROGRAMVERSION :4>v0.2'''
52+
53+
exp_dict = {'ADIF_VER': '3.1.4', 'PROGRAMID': 'Testprog', 'PROGRAMVERSION': 'v0.2'}
54+
self.assertDictEqual(exp_dict, adif_file.adi.unpack(adi_hdr1))
55+
self.assertDictEqual(exp_dict, adif_file.adi.unpack(adi_hdr2))
56+
57+
exp_dict_nostrip = {'ADIF_VER ': '3.1.4', ' PROGRAMID': 'Testprog', ' PROGRAMVERSION ': 'v0.2'}
58+
self.assertDictEqual(exp_dict_nostrip, adif_file.adi.unpack(adi_hdr1, strip_tags=False))
59+
self.assertDictEqual(exp_dict_nostrip, adif_file.adi.unpack(adi_hdr2, strip_tags=False))
60+
4261
def test_20_unpack_record(self):
4362
adi_rec_app = '<APP_TESTAPP_CHANNEL:2:N>24'
4463
adi_rec_name = '<NAME:4>Test'
4564

4665
self.assertDictEqual({'APP_TESTAPP_CHANNEL': '24'}, adif_file.adi.unpack(adi_rec_app))
4766
self.assertDictEqual({'NAME': 'Test'}, adif_file.adi.unpack(adi_rec_name))
4867

68+
def test_25_unpack_record_strip(self):
69+
adi_rec_app = '< APP_TESTAPP_CHANNEL:2:N>24'
70+
adi_rec_name = '< NAME :4>Test'
71+
adi_rec_call = '<CALL :6>XX1XXX'
72+
73+
self.assertDictEqual({'APP_TESTAPP_CHANNEL': '24'}, adif_file.adi.unpack(adi_rec_app))
74+
self.assertDictEqual({'NAME': 'Test'}, adif_file.adi.unpack(adi_rec_name))
75+
self.assertDictEqual({'CALL': 'XX1XXX'}, adif_file.adi.unpack(adi_rec_call))
76+
77+
self.assertDictEqual({' APP_TESTAPP_CHANNEL': '24'}, adif_file.adi.unpack(adi_rec_app, strip_tags=False))
78+
self.assertDictEqual({' NAME ': 'Test'}, adif_file.adi.unpack(adi_rec_name, strip_tags=False))
79+
self.assertDictEqual({'CALL ': 'XX1XXX'}, adif_file.adi.unpack(adi_rec_call, strip_tags=False))
80+
4981
def test_50_goodfile(self):
5082
adi_dict = adif_file.adi.load(get_file_path('testdata/goodfile.txt'))
5183

0 commit comments

Comments
 (0)