DebugClients/Python/coverage/data.py

changeset 5051
3586ebd9fac8
parent 4491
0d8612e24fef
equal deleted inserted replaced
5047:04e5dfbd3f3d 5051:3586ebd9fac8
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3 3
4 """Coverage data for coverage.py.""" 4 """Coverage data for coverage.py."""
5 5
6 import glob 6 import glob
7 import itertools
7 import json 8 import json
8 import optparse 9 import optparse
9 import os 10 import os
10 import os.path 11 import os.path
11 import random 12 import random
12 import re 13 import re
13 import socket 14 import socket
14 import sys
15 15
16 from coverage import env 16 from coverage import env
17 from coverage.backward import iitems, string_class 17 from coverage.backward import iitems, string_class
18 from coverage.debug import _TEST_NAME_FILE 18 from coverage.debug import _TEST_NAME_FILE
19 from coverage.files import PathAliases 19 from coverage.files import PathAliases
20 from coverage.misc import CoverageException, file_be_gone 20 from coverage.misc import CoverageException, file_be_gone, isolate_module
21
22 os = isolate_module(os)
21 23
22 24
23 class CoverageData(object): 25 class CoverageData(object):
24 """Manages collected coverage data, including file storage. 26 """Manages collected coverage data, including file storage.
25 27
175 If the file was executed, returns a list of integers, the line numbers 177 If the file was executed, returns a list of integers, the line numbers
176 executed in the file. The list is in no particular order. 178 executed in the file. The list is in no particular order.
177 179
178 """ 180 """
179 if self._arcs is not None: 181 if self._arcs is not None:
180 if filename in self._arcs: 182 arcs = self._arcs.get(filename)
181 return [s for s, __ in self._arcs[filename] if s > 0] 183 if arcs is not None:
184 all_lines = itertools.chain.from_iterable(arcs)
185 return list(set(l for l in all_lines if l > 0))
182 elif self._lines is not None: 186 elif self._lines is not None:
183 if filename in self._lines: 187 return self._lines.get(filename)
184 return self._lines[filename]
185 return None 188 return None
186 189
187 def arcs(self, filename): 190 def arcs(self, filename):
188 """Get the list of arcs executed for a file. 191 """Get the list of arcs executed for a file.
189 192
268 data = self._read_raw_data(file_obj) 271 data = self._read_raw_data(file_obj)
269 272
270 self._lines = self._arcs = None 273 self._lines = self._arcs = None
271 274
272 if 'lines' in data: 275 if 'lines' in data:
273 self._lines = dict( 276 self._lines = data['lines']
274 (fname.encode(sys.getfilesystemencoding()), linenos)
275 for fname, linenos in iitems(data['lines'])
276 )
277
278 if 'arcs' in data: 277 if 'arcs' in data:
279 self._arcs = dict( 278 self._arcs = dict(
280 (fname.encode(sys.getfilesystemencoding()), 279 (fname, [tuple(pair) for pair in arcs])
281 [tuple(pair) for pair in arcs])
282 for fname, arcs in iitems(data['arcs']) 280 for fname, arcs in iitems(data['arcs'])
283 ) 281 )
284 self._file_tracers = data.get('file_tracers', {}) 282 self._file_tracers = data.get('file_tracers', {})
285 self._runs = data.get('runs', []) 283 self._runs = data.get('runs', [])
286 284
288 286
289 def read_file(self, filename): 287 def read_file(self, filename):
290 """Read the coverage data from `filename` into this object.""" 288 """Read the coverage data from `filename` into this object."""
291 if self._debug and self._debug.should('dataio'): 289 if self._debug and self._debug.should('dataio'):
292 self._debug.write("Reading data from %r" % (filename,)) 290 self._debug.write("Reading data from %r" % (filename,))
293 with self._open_for_reading(filename) as f: 291 try:
294 self.read_fileobj(f) 292 with self._open_for_reading(filename) as f:
293 self.read_fileobj(f)
294 except Exception as exc:
295 raise CoverageException(
296 "Couldn't read data from '%s': %s: %s" % (
297 filename, exc.__class__.__name__, exc,
298 )
299 )
295 300
296 _GO_AWAY = "!coverage.py: This is a private format, don't read it directly!" 301 _GO_AWAY = "!coverage.py: This is a private format, don't read it directly!"
297 302
298 @classmethod 303 @classmethod
299 def _open_for_reading(cls, filename): 304 def _open_for_reading(cls, filename):
431 436
432 # Create the file data. 437 # Create the file data.
433 file_data = {} 438 file_data = {}
434 439
435 if self._has_arcs(): 440 if self._has_arcs():
436 file_data['arcs'] = dict( 441 file_data['arcs'] = self._arcs
437 (fname.decode(sys.getfilesystemencoding()),
438 [tuple(pair) for pair in self._arcs])
439 for fname, arcs in iitems(data['arcs'])
440 )
441 442
442 if self._has_lines(): 443 if self._has_lines():
443 file_data['lines'] = dict( 444 file_data['lines'] = self._lines
444 (fname.decode(sys.getfilesystemencoding()), linenos)
445 for fname, linenos in iitems(self._lines)
446 )
447 445
448 if self._file_tracers: 446 if self._file_tracers:
449 file_data['file_tracers'] = self._file_tracers 447 file_data['file_tracers'] = self._file_tracers
450 448
451 if self._runs: 449 if self._runs:
578 assert isinstance(key, string_class), "Key in _runs shouldn't be %r" % (key,) 576 assert isinstance(key, string_class), "Key in _runs shouldn't be %r" % (key,)
579 577
580 def add_to_hash(self, filename, hasher): 578 def add_to_hash(self, filename, hasher):
581 """Contribute `filename`'s data to the `hasher`. 579 """Contribute `filename`'s data to the `hasher`.
582 580
583 `hasher` is a :class:`coverage.misc.Hasher` instance to be updated with 581 `hasher` is a `coverage.misc.Hasher` instance to be updated with
584 the file's data. It should only get the results data, not the run 582 the file's data. It should only get the results data, not the run
585 data. 583 data.
586 584
587 """ 585 """
588 if self._has_arcs(): 586 if self._has_arcs():
605 603
606 604
607 class CoverageDataFiles(object): 605 class CoverageDataFiles(object):
608 """Manage the use of coverage data files.""" 606 """Manage the use of coverage data files."""
609 607
610 def __init__(self, basename=None): 608 def __init__(self, basename=None, warn=None):
611 """Create a CoverageDataFiles to manage data files. 609 """Create a CoverageDataFiles to manage data files.
612 610
611 `warn` is the warning function to use.
612
613 `basename` is the name of the file to use for storing data. 613 `basename` is the name of the file to use for storing data.
614 614
615 """ 615 """
616 self.warn = warn
616 # Construct the file name that will be used for data storage. 617 # Construct the file name that will be used for data storage.
617 self.filename = os.path.abspath(basename or ".coverage") 618 self.filename = os.path.abspath(basename or ".coverage")
618 619
619 def erase(self, parallel=False): 620 def erase(self, parallel=False):
620 """Erase the data from the file storage. 621 """Erase the data from the file storage.
679 `self.filename` plus dot as a prefix, and those files are combined. 680 `self.filename` plus dot as a prefix, and those files are combined.
680 681
681 If `data_paths` is not provided, then the directory portion of 682 If `data_paths` is not provided, then the directory portion of
682 `self.filename` is used as the directory to search for data files. 683 `self.filename` is used as the directory to search for data files.
683 684
684 Every data file found and combined is then deleted from disk. 685 Every data file found and combined is then deleted from disk. If a file
686 cannot be read, a warning will be issued, and the file will not be
687 deleted.
685 688
686 """ 689 """
687 # Because of the os.path.abspath in the constructor, data_dir will 690 # Because of the os.path.abspath in the constructor, data_dir will
688 # never be an empty string. 691 # never be an empty string.
689 data_dir, local = os.path.split(self.filename) 692 data_dir, local = os.path.split(self.filename)
700 else: 703 else:
701 raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,)) 704 raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
702 705
703 for f in files_to_combine: 706 for f in files_to_combine:
704 new_data = CoverageData() 707 new_data = CoverageData()
705 new_data.read_file(f) 708 try:
706 data.update(new_data, aliases=aliases) 709 new_data.read_file(f)
707 file_be_gone(f) 710 except CoverageException as exc:
711 if self.warn:
712 # The CoverageException has the file name in it, so just
713 # use the message as the warning.
714 self.warn(str(exc))
715 else:
716 data.update(new_data, aliases=aliases)
717 file_be_gone(f)
708 718
709 719
710 def canonicalize_json_data(data): 720 def canonicalize_json_data(data):
711 """Canonicalize our JSON data so it can be compared.""" 721 """Canonicalize our JSON data so it can be compared."""
712 for fname, lines in iitems(data.get('lines', {})): 722 for fname, lines in iitems(data.get('lines', {})):
752 canonicalize_json_data(data) 762 canonicalize_json_data(data)
753 print(pretty_data(data)) 763 print(pretty_data(data))
754 764
755 765
756 if __name__ == '__main__': 766 if __name__ == '__main__':
767 import sys
757 debug_main(sys.argv[1:]) 768 debug_main(sys.argv[1:])
758 769
759 # 770 #
760 # eflag: FileType = Python2 771 # eflag: FileType = Python2

eric ide

mercurial