Skip to content

Commit 215e474

Browse files
committed
sof-tplgreader.py: document boolean precedence and other design issues
Also remove unused imports and add pydocs about data structures. No functional change. Signed-off-by: Marc Herbert <marc.herbert@intel.com>
1 parent ce58aed commit 215e474

1 file changed

Lines changed: 39 additions & 10 deletions

File tree

tools/sof-tplgreader.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#!/usr/bin/env python3
22

3-
import subprocess
4-
import os
5-
import re
63
from tplgtool import TplgParser, TplgFormatter
74
from common import format_pipeline, export_pipeline
85

@@ -117,6 +114,7 @@ def loadFile(self, filename, sofcard=0):
117114
pipeline[comp] = interweaved_dict[comp]
118115
return 0
119116

117+
# The following functions re-implement built-in Python sets
120118
@staticmethod
121119
def list_and(lst1, lst2):
122120
assert(lst1 is not None and lst2 is not None)
@@ -164,10 +162,22 @@ def setField(self, field_lst):
164162
def setBlock(self, block_lst=None):
165163
self._block_lst = block_lst
166164

167-
def _filterOutput(self, target_lst, filter_dict, bIn):
165+
def _filterOutput(self, pipelines, filter_dict, bIn):
166+
"""Returns the subset of pipelines matching a single filter expression.
167+
168+
pipelines: list of pipelines. Each pipeline is a dict, example:
169+
{'cap_name': 'Low Latency Playback 0', 'dev': 'hw:0,6',...
170+
171+
filter_dict: filter like for instance { 'id' : ['3'] }. It's not
172+
clear what happens if the dict has several elements; avoid?
173+
174+
bIn: True: exclude filter, False: include filter.
175+
"""
168176
filtered = []
169-
for line in target_lst[:]:
170-
check = False
177+
for line in pipelines[:]:
178+
check = False # "check" means "match"
179+
# This inner for loop is deceiving: the filter_dict usually has a
180+
# single key:value and then the break/else is pure confusion.
171181
for key, value in filter_dict.items():
172182
if 'any' in value or value == ['']:
173183
check = True if key in line.keys() else False
@@ -177,21 +187,25 @@ def _filterOutput(self, target_lst, filter_dict, bIn):
177187
if check is bIn:
178188
break
179189
else:
190+
# No 'break': include this pipeline
180191
filtered.append(line)
181192
return filtered
182193

183194
def _filter_by_dict(self, filter_dict):
195+
"filter_dict comes from parse_filter(), see filter_dict example there."
184196
if filter_dict == {}:
185197
return self._pipeline_lst
186198
full_list = self._pipeline_lst
187199
filtered = None
188-
# pipelines filtered by the first filter item
200+
# Apply the first expression in the filter
189201
for key, value in filter_dict['filter'][0].items():
190202
if key.startswith('~'):
191203
filtered = self._filterOutput(full_list, {key[1:]:value}, True)
192204
else:
193205
filtered = self._filterOutput(full_list, {key:value}, False)
194-
# do filtering by the rest filter items and logic operations
206+
# Combine with the remaining expressions and boolean
207+
# operators. Apply the filters from left to right, no usual
208+
# boolean precedence. See --help below.
195209
for idx in range(1, len(filter_dict['filter'])):
196210
new_filtered = None
197211
for key, value in filter_dict['filter'][idx].items():
@@ -251,9 +265,15 @@ def func_getPipeline(tplgObj, tplgName, sdcard_id, sort):
251265
exit(1)
252266
return tplgObj.getPipeline(sort)
253267

254-
# parse filter string into two structures, one is dict list for each filter item,
255-
# and another is logic operation list.
256268
def parse_filter(filter_str):
269+
"""Parse filter input into two structures, one is dict list for each
270+
filter item, and another is logic operation list. For example
271+
"id:2 | pga & id:1" is parsed as:
272+
273+
{ 'filter': [ {'id': ['2']}, {'pga': ['']}, {'id': ['1']} ],
274+
'op': [ '|', '&' ]
275+
}
276+
"""
257277
filter_lst = []
258278
op_lst = []
259279
for filter_elem in filter_str.split('|'):
@@ -285,12 +305,21 @@ def parse_and_set_block_keyword(block_str, tplgreader):
285305
string format is 'key':'value','value', the filter
286306
string support & | and ~ logic operation.
287307
if only care about key, you can use 'key':'any'.
308+
288309
Example Usage:
289310
`-f "type:any` -> all pipelines
290311
`-f "type:playback"` -> playback pipelines
291312
`-f "type:capture & pga"` -> capture pipelines with PGA
292313
`-f "pga & eq"` -> pipelines with both EQ and PGA
293314
`-f "id:3"` -> pipeline whose id is 3
315+
316+
WARNING: usual boolean precedence does NOT apply! Filters are naively
317+
applied from left to right. For instance you could expect "id:2 | pga & id:1"
318+
to be interpreted as "id:2 | (pga & id:1)" and to return nothing.
319+
"id" cannot be both 1 and 2. But no: this script applies it from
320+
left to right like this: "(id:2 | pga) & id:1" so it is equivalent to
321+
"pga & id:1"
322+
294323
''')
295324
parser.add_argument('-b', '--block', type=str,
296325
help='setup block filter, command line parameter format is the same as -f argument')

0 commit comments

Comments
 (0)