summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/backward.py13
-rw-r--r--coverage/collector.py20
2 files changed, 28 insertions, 5 deletions
diff --git a/coverage/backward.py b/coverage/backward.py
index 62ca495f..5aff6406 100644
--- a/coverage/backward.py
+++ b/coverage/backward.py
@@ -59,18 +59,29 @@ except ImportError:
# in Python versions earlier than 3.3.
from pipes import quote as shlex_quote
-# A function to iterate listlessly over a dict's items.
+# A function to iterate listlessly over a dict's items, and one to get the
+# items as a list.
try:
{}.iteritems
except AttributeError:
+ # Python 3
def iitems(d):
"""Produce the items from dict `d`."""
return d.items()
+
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return list(d.items())
else:
+ # Python 2
def iitems(d):
"""Produce the items from dict `d`."""
return d.iteritems()
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return d.items()
+
# Getting the `next` function from an iterator is different in 2 and 3.
try:
iter([]).next
diff --git a/coverage/collector.py b/coverage/collector.py
index 70111d39..658aed62 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -7,7 +7,7 @@ import os
import sys
from coverage import env
-from coverage.backward import iitems
+from coverage.backward import litems, range # pylint: disable=redefined-builtin
from coverage.debug import short_stack
from coverage.files import abs_file
from coverage.misc import CoverageException, isolate_module
@@ -382,10 +382,22 @@ class Collector(object):
def abs_file_dict(d):
"""Return a dict like d, but with keys modified by `abs_file`."""
- # The call to list() ensures that the GIL protects the dictionary
+ # The call to litems() ensures that the GIL protects the dictionary
# iterator against concurrent modifications by tracers running
- # in other threads.
- return dict((abs_file(k), v) for k, v in list(iitems(d)))
+ # in other threads. We try three times in case of concurrent
+ # access, hoping to get a clean copy.
+ runtime_err = None
+ for _ in range(3):
+ try:
+ items = litems(d)
+ except RuntimeError as ex:
+ runtime_err = ex
+ else:
+ break
+ else:
+ raise runtime_err # pylint: disable=raising-bad-type
+
+ return dict((abs_file(k), v) for k, v in items)
if self.branch:
covdata.add_arcs(abs_file_dict(self.data))