Skip to content

Commit d402b59

Browse files
committed
Handle multiple iterations
1 parent a6b92ae commit d402b59

3 files changed

Lines changed: 41 additions & 4 deletions

File tree

‎addons/cppcheckdata.py‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,11 @@ class CppcheckData:
13861386
suppressions List of Suppressions
13871387
files Source files for elements occurred in this configuration
13881388
1389+
The configurations property loads all configurations once and keeps them,
1390+
which is best when the configurations are iterated more than once.
1391+
iterconfigurations() parses the dump file on each call but creates one
1392+
configuration at a time, which uses less memory for a single iteration.
1393+
13891394
To iterate through all configurations use such code:
13901395
@code
13911396
data = cppcheckdata.parsedump(...)
@@ -1424,6 +1429,7 @@ def __init__(self, filename):
14241429
self.platform = None
14251430
self.suppressions = []
14261431
self.files = [] # source files for elements occurred in this configuration
1432+
self._configurations = None # set by the configurations property
14271433

14281434
# Parse general configuration options from <dumps> node, the raw tokens
14291435
# and the suppressions. The configurations are parsed by iterconfigurations.
@@ -1444,14 +1450,26 @@ def __init__(self, filename):
14441450
def configurations(self):
14451451
"""
14461452
Return the list of all available Configuration objects.
1453+
The configurations are loaded on the first access and kept, so later
1454+
accesses and iterconfigurations() do not parse the dump file again.
1455+
All configurations stay in memory as long as this object exists.
14471456
"""
1448-
return list(self.iterconfigurations())
1457+
if self._configurations is None:
1458+
self._configurations = list(self.iterconfigurations())
1459+
return self._configurations
14491460

14501461
def iterconfigurations(self):
14511462
"""
14521463
Create and return iterator for the available Configuration objects.
14531464
The iterator loops over all Configurations in the dump file tree, in document order.
1465+
If the configurations property has been used then the kept configurations
1466+
are returned. Otherwise the dump file is parsed and each configuration is
1467+
created when the iterator reaches it, so a configuration can be freed when
1468+
the caller is done with it. Each call parses the dump file again.
14541469
"""
1470+
if self._configurations is not None:
1471+
yield from self._configurations
1472+
return
14551473
# Parse the dump file incrementally to avoid large memory consumption.
14561474
# A configuration is returned as soon as its </dump> has been parsed.
14571475
# The garbage collector is only disabled while parsing, not while the

‎addons/misc.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def isStringLiteral(tokenString):
4848
def stringConcatInArrayInit(data):
4949
# Get all string macros
5050
stringMacros = []
51-
for cfg in data.iterconfigurations():
51+
for cfg in data.configurations:
5252
for directive in cfg.directives:
5353
res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str)
5454
if res:
@@ -76,7 +76,7 @@ def stringConcatInArrayInit(data):
7676

7777

7878
def implicitlyVirtual(data):
79-
for cfg in data.iterconfigurations():
79+
for cfg in data.configurations:
8080
for function in cfg.functions:
8181
if function.isImplicitlyVirtual is None:
8282
continue
@@ -85,7 +85,7 @@ def implicitlyVirtual(data):
8585
reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual')
8686

8787
def ellipsisStructArg(data):
88-
for cfg in data.iterconfigurations():
88+
for cfg in data.configurations:
8989
for tok in cfg.tokenlist:
9090
if tok.str != '(':
9191
continue

‎test/addon/test_cppcheckdata.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,25 @@ def test_iterconfigurations(self, multi_cfg_data):
9393
assert not find_tokens(cfgs[0], 'foo')
9494
assert find_tokens(cfgs[1], 'foo')
9595

96+
def test_configurations_kept(self, multi_cfg_data):
97+
data = cppcheckdata.parsedump(multi_cfg_data.filename)
98+
cfgs = data.configurations
99+
assert len(cfgs) == 2
100+
# later accesses and iterconfigurations() return the same objects
101+
assert data.configurations is cfgs
102+
iterated = list(data.iterconfigurations())
103+
assert len(iterated) == 2
104+
assert iterated[0] is cfgs[0]
105+
assert iterated[1] is cfgs[1]
106+
107+
def test_iterconfigurations_not_kept(self, multi_cfg_data):
108+
data = cppcheckdata.parsedump(multi_cfg_data.filename)
109+
first = next(data.iterconfigurations())
110+
# without the configurations property each call parses the dump file again
111+
assert next(data.iterconfigurations()) is not first
112+
assert len(data.configurations) == 2
113+
assert data.configurations[0] is not first
114+
96115
def test_standards(self, sample_cfg, sample_cpp_cfg):
97116
assert sample_cfg.standards.c.startswith('c')
98117
assert sample_cpp_cfg.standards.cpp.startswith('c++')

0 commit comments

Comments
 (0)