eric7/DebugClients/Python/coverage/parser.py

branch
eric7
changeset 8527
2bd1325d727e
parent 8312
800c432b34c8
child 8775
0802ae193343
--- a/eric7/DebugClients/Python/coverage/parser.py	Fri Aug 20 19:56:17 2021 +0200
+++ b/eric7/DebugClients/Python/coverage/parser.py	Sat Aug 21 14:21:44 2021 +0200
@@ -205,6 +205,12 @@
         if not empty:
             self.raw_statements.update(self.byte_parser._find_statements())
 
+        # The first line of modules can lie and say 1 always, even if the first
+        # line of code is later. If so, map 1 to the actual first line of the
+        # module.
+        if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
+            self._multiline[1] = min(self.raw_statements)
+
     def first_line(self, line):
         """Return the first line number of the statement including `line`."""
         if line < 0:
@@ -220,7 +226,7 @@
         Returns a set of the first lines.
 
         """
-        return set(self.first_line(l) for l in lines)
+        return {self.first_line(l) for l in lines}
 
     def translate_lines(self, lines):
         """Implement `FileReporter.translate_lines`."""
@@ -332,9 +338,7 @@
         fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
 
         msgs = []
-        for fragment_pair in fragment_pairs:
-            smsg, emsg = fragment_pair
-
+        for smsg, emsg in fragment_pairs:
             if emsg is None:
                 if end < 0:
                     # Hmm, maybe we have a one-line callable, let's check.
@@ -389,34 +393,35 @@
         """
         return (ByteParser(self.text, code=c) for c in code_objects(self.code))
 
-    def _bytes_lines(self):
-        """Map byte offsets to line numbers in `code`.
-
-        Uses co_lnotab described in Python/compile.c to map byte offsets to
-        line numbers.  Produces a sequence: (b0, l0), (b1, l1), ...
+    def _line_numbers(self):
+        """Yield the line numbers possible in this code object.
 
-        Only byte offsets that correspond to line numbers are included in the
-        results.
-
+        Uses co_lnotab described in Python/compile.c to find the
+        line numbers.  Produces a sequence: l0, l1, ...
         """
-        # Adapted from dis.py in the standard library.
-        byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
-        line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
+        if hasattr(self.code, "co_lines"):
+            for _, _, line in self.code.co_lines():
+                if line is not None:
+                    yield line
+        else:
+            # Adapted from dis.py in the standard library.
+            byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+            line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
 
-        last_line_num = None
-        line_num = self.code.co_firstlineno
-        byte_num = 0
-        for byte_incr, line_incr in zip(byte_increments, line_increments):
-            if byte_incr:
-                if line_num != last_line_num:
-                    yield (byte_num, line_num)
-                    last_line_num = line_num
-                byte_num += byte_incr
-            if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
-                line_incr -= 0x100
-            line_num += line_incr
-        if line_num != last_line_num:
-            yield (byte_num, line_num)
+            last_line_num = None
+            line_num = self.code.co_firstlineno
+            byte_num = 0
+            for byte_incr, line_incr in zip(byte_increments, line_increments):
+                if byte_incr:
+                    if line_num != last_line_num:
+                        yield line_num
+                        last_line_num = line_num
+                    byte_num += byte_incr
+                if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
+                    line_incr -= 0x100
+                line_num += line_incr
+            if line_num != last_line_num:
+                yield line_num
 
     def _find_statements(self):
         """Find the statements in `self.code`.
@@ -427,7 +432,7 @@
         """
         for bp in self.child_parsers():
             # Get all of the lineno information from this code.
-            for _, l in bp._bytes_lines():
+            for l in bp._line_numbers():
                 yield l
 
 
@@ -520,7 +525,7 @@
     def __init__(self, text, statements, multiline):
         self.root_node = ast.parse(neuter_encoding_declaration(text))
         # TODO: I think this is happening in too many places.
-        self.statements = set(multiline.get(l, l) for l in statements)
+        self.statements = {multiline.get(l, l) for l in statements}
         self.multiline = multiline
 
         if AST_DUMP:                                # pragma: debugging
@@ -619,17 +624,19 @@
             return node.lineno
 
     def _line__Module(self, node):
-        if node.body:
+        if env.PYBEHAVIOR.module_firstline_1:
+            return 1
+        elif node.body:
             return self.line_for_node(node.body[0])
         else:
             # Empty modules have no line number, they always start at 1.
             return 1
 
     # The node types that just flow to the next node with no complications.
-    OK_TO_DEFAULT = set([
+    OK_TO_DEFAULT = {
         "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
         "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
-    ])
+    }
 
     @contract(returns='ArcStarts')
     def add_arcs(self, node):
@@ -661,7 +668,7 @@
                     print("*** Unhandled: {}".format(node))
 
             # Default for simple statements: one exit from this node.
-            return set([ArcStart(self.line_for_node(node))])
+            return {ArcStart(self.line_for_node(node))}
 
     @one_of("from_start, prev_starts")
     @contract(returns='ArcStarts')
@@ -677,7 +684,7 @@
 
         """
         if prev_starts is None:
-            prev_starts = set([from_start])
+            prev_starts = {from_start}
         for body_node in body:
             lineno = self.line_for_node(body_node)
             first_line = self.multiline.get(lineno, lineno)
@@ -890,7 +897,7 @@
                         self.add_arc(last, lineno)
                         last = lineno
         # The body is handled in collect_arcs.
-        return set([ArcStart(last)])
+        return {ArcStart(last)}
 
     _handle__ClassDef = _handle_decorated
 
@@ -984,7 +991,7 @@
                 # If there are `except` clauses, then raises in the try body
                 # will already jump to them.  Start this set over for raises in
                 # `except` and `else`.
-                try_block.raise_from = set([])
+                try_block.raise_from = set()
         else:
             self.block_stack.pop()
 
@@ -1079,7 +1086,7 @@
             if start.cause is not None:
                 causes.append(start.cause.format(lineno=start.lineno))
         cause = " or ".join(causes)
-        exits = set(ArcStart(xit.lineno, cause) for xit in exits)
+        exits = {ArcStart(xit.lineno, cause) for xit in exits}
         return exits
 
     @contract(returns='ArcStarts')
@@ -1109,9 +1116,14 @@
 
     @contract(returns='ArcStarts')
     def _handle__While(self, node):
+        start = to_top = self.line_for_node(node.test)
         constant_test = self.is_constant_expr(node.test)
-        start = to_top = self.line_for_node(node.test)
+        top_is_body0 = False
         if constant_test and (env.PY3 or constant_test == "Num"):
+            top_is_body0 = True
+        if env.PYBEHAVIOR.keep_constant_test:
+            top_is_body0 = False
+        if top_is_body0:
             to_top = self.line_for_node(node.body[0])
         self.block_stack.append(LoopBlock(start=to_top))
         from_start = ArcStart(start, cause="the condition on line {lineno} was never true")

eric ide

mercurial