diff -r d856023fbeb0 -r fcca2fa618bf eric7/DebugClients/Python/coverage/sqldata.py --- a/eric7/DebugClients/Python/coverage/sqldata.py Sun Jan 16 20:28:42 2022 +0100 +++ b/eric7/DebugClients/Python/coverage/sqldata.py Sat Jan 22 14:44:56 2022 +0100 @@ -12,16 +12,18 @@ import glob import itertools import os +import random import re +import socket import sqlite3 import sys import threading import zlib from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr -from coverage.exceptions import CoverageException +from coverage.exceptions import CoverageException, DataError from coverage.files import PathAliases -from coverage.misc import contract, file_be_gone, filename_suffix, isolate_module +from coverage.misc import contract, file_be_gone, isolate_module from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits from coverage.version import __version__ @@ -191,7 +193,7 @@ Arguments: basename (str): the base name of the data file, defaulting to - ".coverage". + ".coverage". This can be a path to a file in another directory. suffix (str or bool): has the same meaning as the `data_suffix` argument to :class:`coverage.Coverage`. no_disk (bool): if True, keep all data in memory, and don't @@ -287,14 +289,14 @@ try: schema_version, = db.execute_one("select version from coverage_schema") except Exception as exc: - raise CoverageException( + raise DataError( "Data file {!r} doesn't seem to be a coverage data file: {}".format( self._filename, exc ) ) from exc else: if schema_version != SCHEMA_VERSION: - raise CoverageException( + raise DataError( "Couldn't use data file {!r}: wrong schema: {} instead of {}".format( self._filename, schema_version, SCHEMA_VERSION ) @@ -316,7 +318,7 @@ self._create_db() return self._dbs[threading.get_ident()] - def __nonzero__(self): + def __bool__(self): if (threading.get_ident() not in self._dbs and not os.path.exists(self._filename)): return False try: @@ -326,8 +328,6 @@ except CoverageException: return False - __bool__ = __nonzero__ - @contract(returns="bytes") def dumps(self): """Serialize the current data to a byte string. @@ -370,9 +370,9 @@ if self._debug.should("dataio"): self._debug.write(f"Loading data into data file {self._filename!r}") if data[:1] != b"z": - raise CoverageException( + raise DataError( f"Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)" - ) + ) script = zlib.decompress(data[1:]).decode("utf-8") self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug) with db: @@ -513,9 +513,9 @@ assert lines or arcs assert not (lines and arcs) if lines and self._has_arcs: - raise CoverageException("Can't add line measurements to existing branch data") + raise DataError("Can't add line measurements to existing branch data") if arcs and self._has_lines: - raise CoverageException("Can't add branch measurements to existing line data") + raise DataError("Can't add branch measurements to existing line data") if not self._has_arcs and not self._has_lines: self._has_lines = lines self._has_arcs = arcs @@ -541,14 +541,14 @@ for filename, plugin_name in file_tracers.items(): file_id = self._file_id(filename) if file_id is None: - raise CoverageException( + raise DataError( f"Can't add file tracer data for unmeasured file '{filename}'" ) existing_plugin = self.file_tracer(filename) if existing_plugin: if existing_plugin != plugin_name: - raise CoverageException( + raise DataError( "Conflicting file tracer name for '{}': {!r} vs {!r}".format( filename, existing_plugin, plugin_name, ) @@ -578,7 +578,7 @@ self._start_using() with self._connect(): # Use this to get one transaction. if not self._has_arcs and not self._has_lines: - raise CoverageException("Can't touch files in an empty CoverageData") + raise DataError("Can't touch files in an empty CoverageData") for filename in filenames: self._file_id(filename, add=True) @@ -597,9 +597,9 @@ getattr(other_data, "_filename", "???"), )) if self._has_lines and other_data._has_arcs: - raise CoverageException("Can't combine arc data with line data") + raise DataError("Can't combine arc data with line data") if self._has_arcs and other_data._has_lines: - raise CoverageException("Can't combine line data with arc data") + raise DataError("Can't combine line data with arc data") aliases = aliases or PathAliases() @@ -692,7 +692,7 @@ other_tracer = tracers.get(path, "") # If there is no tracer, there is always the None tracer. if this_tracer is not None and this_tracer != other_tracer: - raise CoverageException( + raise DataError( "Conflicting file tracer name for '{}': {!r} vs {!r}".format( path, this_tracer, other_tracer ) @@ -1004,6 +1004,26 @@ ] +def filename_suffix(suffix): + """Compute a filename suffix for a data file. + + If `suffix` is a string or None, simply return it. If `suffix` is True, + then build a suffix incorporating the hostname, process id, and a random + number. + + Returns a string or None. + + """ + if suffix is True: + # If data_suffix was a simple true value, then make a suffix with + # plenty of distinguishing information. We do this here in + # `save()` at the last minute so that the pid will be correct even + # if the process forks. + dice = random.Random(os.urandom(8)).randint(0, 999999) + suffix = "%s.%s.%06d" % (socket.gethostname(), os.getpid(), dice) + return suffix + + class SqliteDb(SimpleReprMixin): """A simple abstraction over a SQLite database. @@ -1035,7 +1055,7 @@ try: self.con = sqlite3.connect(self.filename, check_same_thread=False) except sqlite3.Error as exc: - raise CoverageException(f"Couldn't use data file {self.filename!r}: {exc}") from exc + raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc self.con.create_function("REGEXP", 2, _regexp) @@ -1068,7 +1088,7 @@ except Exception as exc: if self.debug: self.debug.write(f"EXCEPTION from __exit__: {exc}") - raise CoverageException(f"Couldn't end data file {self.filename!r}: {exc}") from exc + raise DataError(f"Couldn't end data file {self.filename!r}: {exc}") from exc def execute(self, sql, parameters=()): """Same as :meth:`python:sqlite3.Connection.execute`.""" @@ -1099,7 +1119,7 @@ pass if self.debug: self.debug.write(f"EXCEPTION from execute: {msg}") - raise CoverageException(f"Couldn't use data file {self.filename!r}: {msg}") from exc + raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc def execute_one(self, sql, parameters=()): """Execute a statement and return the one row that results.