8
8
9
9
import collections
10
10
import datetime
11
+ import functools
11
12
import glob
12
13
import itertools
13
14
import os
@@ -179,6 +180,10 @@ class CoverageData(SimpleReprMixin):
179
180
Data in a :class:`CoverageData` can be serialized and deserialized with
180
181
:meth:`dumps` and :meth:`loads`.
181
182
183
+ The methods used during the coverage.py collection phase
184
+ (:meth:`add_lines`, :meth:`add_arcs`, :meth:`set_context`, and
185
+ :meth:`add_file_tracers`) are thread-safe. Other methods may not be.
186
+
182
187
"""
183
188
184
189
def __init__ (self , basename = None , suffix = None , no_disk = False , warn = None , debug = None ):
@@ -207,6 +212,8 @@ def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=N
207
212
# Maps thread ids to SqliteDb objects.
208
213
self ._dbs = {}
209
214
self ._pid = os .getpid ()
215
+ # Synchronize the operations used during collection.
216
+ self ._lock = threading .Lock ()
210
217
211
218
# Are we in sync with the data file?
212
219
self ._have_used = False
@@ -218,6 +225,15 @@ def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=N
218
225
self ._current_context_id = None
219
226
self ._query_context_ids = None
220
227
228
+ def _locked (method ): # pylint: disable=no-self-argument
229
+ """A decorator for methods that should hold self._lock."""
230
+ @functools .wraps (method )
231
+ def _wrapped (self , * args , ** kwargs ):
232
+ with self ._lock :
233
+ # pylint: disable=not-callable
234
+ return method (self , * args , ** kwargs )
235
+ return _wrapped
236
+
221
237
def _choose_filename (self ):
222
238
"""Set self._filename based on inited attributes."""
223
239
if self ._no_disk :
@@ -388,6 +404,7 @@ def _context_id(self, context):
388
404
else :
389
405
return None
390
406
407
+ @_locked
391
408
def set_context (self , context ):
392
409
"""Set the current context for future :meth:`add_lines` etc.
393
410
@@ -429,6 +446,7 @@ def data_filename(self):
429
446
"""
430
447
return self ._filename
431
448
449
+ @_locked
432
450
def add_lines (self , line_data ):
433
451
"""Add measured line data.
434
452
@@ -461,6 +479,7 @@ def add_lines(self, line_data):
461
479
(file_id , self ._current_context_id , linemap ),
462
480
)
463
481
482
+ @_locked
464
483
def add_arcs (self , arc_data ):
465
484
"""Add measured arc data.
466
485
@@ -505,6 +524,7 @@ def _choose_lines_or_arcs(self, lines=False, arcs=False):
505
524
('has_arcs' , str (int (arcs )))
506
525
)
507
526
527
+ @_locked
508
528
def add_file_tracers (self , file_tracers ):
509
529
"""Add per-file plugin information.
510
530
0 commit comments