coverage 7.10.7__cp314-cp314-win_arm64.whl → 7.11.0__cp314-cp314-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of coverage might be problematic. Click here for more details.

coverage/cmdline.py CHANGED
@@ -24,7 +24,7 @@ from coverage.control import DEFAULT_DATAFILE
24
24
  from coverage.core import CTRACER_FILE
25
25
  from coverage.data import combinable_files, debug_data_file
26
26
  from coverage.debug import info_header, short_stack, write_formatted_info
27
- from coverage.exceptions import NoSource, _BaseCoverageException, _ExceptionDuringRun
27
+ from coverage.exceptions import NoSource, CoverageException, _ExceptionDuringRun
28
28
  from coverage.execfile import PyRunner
29
29
  from coverage.results import display_covered, should_fail_under
30
30
  from coverage.version import __url__
@@ -1139,7 +1139,7 @@ def main(argv: list[str] | None = None) -> int | None:
1139
1139
  # exception.
1140
1140
  traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
1141
1141
  status = ERR
1142
- except _BaseCoverageException as err:
1142
+ except CoverageException as err:
1143
1143
  # A controlled error inside coverage.py: print the message to the user.
1144
1144
  msg = err.args[0]
1145
1145
  if err.slug:
coverage/collector.py CHANGED
@@ -146,12 +146,12 @@ class Collector:
146
146
  self.concur_id_func = greenlet.getcurrent
147
147
  elif "eventlet" in concurrencies:
148
148
  tried = "eventlet"
149
- import eventlet.greenthread # pylint: disable=import-error,useless-suppression
149
+ import eventlet.greenthread
150
150
 
151
151
  self.concur_id_func = eventlet.greenthread.getcurrent
152
152
  elif "gevent" in concurrencies:
153
153
  tried = "gevent"
154
- import gevent # pylint: disable=import-error,useless-suppression
154
+ import gevent
155
155
 
156
156
  self.concur_id_func = gevent.getcurrent
157
157
 
coverage/config.py CHANGED
@@ -14,7 +14,7 @@ import os
14
14
  import os.path
15
15
  import re
16
16
  from collections.abc import Iterable
17
- from typing import Any, Callable, Final, Mapping, Union
17
+ from typing import Any, Callable, Final, Mapping
18
18
 
19
19
  from coverage.exceptions import ConfigError
20
20
  from coverage.misc import human_sorted_items, isolate_module, substitute_variables
@@ -143,7 +143,7 @@ class HandyConfigParser(configparser.ConfigParser):
143
143
  return process_regexlist(section, option, line_list.splitlines())
144
144
 
145
145
 
146
- TConfigParser = Union[HandyConfigParser, TomlConfigParser]
146
+ TConfigParser = HandyConfigParser | TomlConfigParser
147
147
 
148
148
 
149
149
  # The default line exclusion regexes.
coverage/control.py CHANGED
@@ -19,7 +19,7 @@ import time
19
19
  import warnings
20
20
  from collections.abc import Iterable, Iterator
21
21
  from types import FrameType
22
- from typing import IO, Any, Callable, Union, cast
22
+ from typing import IO, Any, Callable, cast
23
23
 
24
24
  from coverage import env
25
25
  from coverage.annotate import AnnotateReporter
@@ -298,7 +298,7 @@ class Coverage(TConfigurable):
298
298
  self._debug: DebugControl = NoDebugging()
299
299
  self._inorout: InOrOut | None = None
300
300
  self._plugins: Plugins = Plugins()
301
- self._plugin_override = cast(Union[Iterable[TCoverageInit], None], plugins)
301
+ self._plugin_override = cast(Iterable[TCoverageInit] | None, plugins)
302
302
  self._data: CoverageData | None = None
303
303
  self._data_to_close: list[CoverageData] = []
304
304
  self._core: Core | None = None
coverage/debug.py CHANGED
@@ -155,14 +155,15 @@ def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]:
155
155
  if data == []:
156
156
  data = "-none-"
157
157
  prefix = f"{label:>{LABEL_LEN}}: "
158
- if isinstance(data, tuple) and len(str(data)) < 30:
159
- yield f"{prefix}{data}"
160
- elif isinstance(data, (list, set, tuple)):
161
- for e in data:
162
- yield f"{prefix}{e}"
163
- prefix = " " * (LABEL_LEN + 2)
164
- else:
165
- yield f"{prefix}{data}"
158
+ match data:
159
+ case tuple() if len(str(data)) < 30:
160
+ yield f"{prefix}{data}"
161
+ case tuple() | list() | set():
162
+ for e in data:
163
+ yield f"{prefix}{e}"
164
+ prefix = " " * (LABEL_LEN + 2)
165
+ case _:
166
+ yield f"{prefix}{data}"
166
167
 
167
168
 
168
169
  def write_formatted_info(
coverage/env.py CHANGED
@@ -43,7 +43,7 @@ else:
43
43
  GIL = getattr(sys, "_is_gil_enabled", lambda: True)()
44
44
 
45
45
  # Do we ship compiled coveragepy wheels for this version?
46
- SHIPPING_WHEELS = CPYTHON and PYVERSION[:2] <= (3, 13)
46
+ SHIPPING_WHEELS = CPYTHON and PYVERSION[:2] <= (3, 14)
47
47
 
48
48
  # Should we default to sys.monitoring?
49
49
  SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14)
@@ -53,63 +53,6 @@ SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14)
53
53
  class PYBEHAVIOR:
54
54
  """Flags indicating this Python's behavior."""
55
55
 
56
- # Does Python conform to PEP626, Precise line numbers for debugging and other tools.
57
- # https://www.python.org/dev/peps/pep-0626
58
- pep626 = (PYVERSION > (3, 10, 0, "alpha", 4)) # fmt: skip
59
-
60
- # Is "if __debug__" optimized away?
61
- optimize_if_debug = not pep626
62
-
63
- # Is "if not __debug__" optimized away? The exact details have changed
64
- # across versions.
65
- optimize_if_not_debug = 1 if pep626 else 2
66
-
67
- # 3.7 changed how functions with only docstrings are numbered.
68
- docstring_only_function = (not PYPY) and (PYVERSION <= (3, 10))
69
-
70
- # Lines after break/continue/return/raise are no longer compiled into the
71
- # bytecode. They used to be marked as missing, now they aren't executable.
72
- omit_after_jump = pep626 or PYPY
73
-
74
- # PyPy has always omitted statements after return.
75
- omit_after_return = omit_after_jump or PYPY
76
-
77
- # Optimize away unreachable try-else clauses.
78
- optimize_unreachable_try_else = pep626
79
-
80
- # Modules used to have firstlineno equal to the line number of the first
81
- # real line of code. Now they always start at 1.
82
- module_firstline_1 = pep626
83
-
84
- # Are "if 0:" lines (and similar) kept in the compiled code?
85
- keep_constant_test = pep626
86
-
87
- # When leaving a with-block, do we visit the with-line again for the exit?
88
- # For example, wwith.py:
89
- #
90
- # with open("/tmp/test", "w") as f1:
91
- # a = 2
92
- # with open("/tmp/test2", "w") as f3:
93
- # print(4)
94
- #
95
- # % python3.9 -m trace -t wwith.py | grep wwith
96
- # --- modulename: wwith, funcname: <module>
97
- # wwith.py(1): with open("/tmp/test", "w") as f1:
98
- # wwith.py(2): a = 2
99
- # wwith.py(3): with open("/tmp/test2", "w") as f3:
100
- # wwith.py(4): print(4)
101
- #
102
- # % python3.10 -m trace -t wwith.py | grep wwith
103
- # --- modulename: wwith, funcname: <module>
104
- # wwith.py(1): with open("/tmp/test", "w") as f1:
105
- # wwith.py(2): a = 2
106
- # wwith.py(3): with open("/tmp/test2", "w") as f3:
107
- # wwith.py(4): print(4)
108
- # wwith.py(3): with open("/tmp/test2", "w") as f3:
109
- # wwith.py(1): with open("/tmp/test", "w") as f1:
110
- #
111
- exit_through_with = (PYVERSION >= (3, 10, 0, "beta")) # fmt: skip
112
-
113
56
  # When leaving a with-block, do we visit the with-line exactly,
114
57
  # or the context managers in inner-out order?
115
58
  #
@@ -147,12 +90,6 @@ class PYBEHAVIOR:
147
90
 
148
91
  exit_with_through_ctxmgr = (PYVERSION >= (3, 12, 6)) # fmt: skip
149
92
 
150
- # Match-case construct.
151
- match_case = (PYVERSION >= (3, 10)) # fmt: skip
152
-
153
- # Some words are keywords in some places, identifiers in other places.
154
- soft_keywords = (PYVERSION >= (3, 10)) # fmt: skip
155
-
156
93
  # f-strings are parsed as code, pep 701
157
94
  fstring_syntax = (PYVERSION >= (3, 12)) # fmt: skip
158
95
 
coverage/exceptions.py CHANGED
@@ -8,21 +8,15 @@ from __future__ import annotations
8
8
  from typing import Any
9
9
 
10
10
 
11
- class _BaseCoverageException(Exception):
12
- """The base-base of all Coverage exceptions."""
11
+ class CoverageException(Exception):
12
+ """The base class of all exceptions raised by Coverage.py."""
13
13
 
14
14
  def __init__(self, *args: Any, slug: str | None = None) -> None:
15
15
  super().__init__(*args)
16
16
  self.slug = slug
17
17
 
18
18
 
19
- class CoverageException(_BaseCoverageException):
20
- """The base class of all exceptions raised by Coverage.py."""
21
-
22
- pass
23
-
24
-
25
- class ConfigError(_BaseCoverageException):
19
+ class ConfigError(CoverageException):
26
20
  """A problem with a config file, or a value in one."""
27
21
 
28
22
  pass
coverage/misc.py CHANGED
@@ -163,31 +163,32 @@ class Hasher:
163
163
  def update(self, v: Any) -> None:
164
164
  """Add `v` to the hash, recursively if needed."""
165
165
  self.hash.update(str(type(v)).encode("utf-8"))
166
- if isinstance(v, str):
167
- self.hash.update(v.encode("utf-8"))
168
- elif isinstance(v, bytes):
169
- self.hash.update(v)
170
- elif v is None:
171
- pass
172
- elif isinstance(v, (int, float)):
173
- self.hash.update(str(v).encode("utf-8"))
174
- elif isinstance(v, (tuple, list)):
175
- for e in v:
176
- self.update(e)
177
- elif isinstance(v, dict):
178
- keys = v.keys()
179
- for k in sorted(keys):
180
- self.update(k)
181
- self.update(v[k])
182
- else:
183
- for k in dir(v):
184
- if k.startswith("__"):
185
- continue
186
- a = getattr(v, k)
187
- if inspect.isroutine(a):
188
- continue
189
- self.update(k)
190
- self.update(a)
166
+ match v:
167
+ case None:
168
+ pass
169
+ case str():
170
+ self.hash.update(v.encode("utf-8"))
171
+ case bytes():
172
+ self.hash.update(v)
173
+ case int() | float():
174
+ self.hash.update(str(v).encode("utf-8"))
175
+ case tuple() | list():
176
+ for e in v:
177
+ self.update(e)
178
+ case dict():
179
+ keys = v.keys()
180
+ for k in sorted(keys):
181
+ self.update(k)
182
+ self.update(v[k])
183
+ case _:
184
+ for k in dir(v):
185
+ if k.startswith("__"):
186
+ continue
187
+ a = getattr(v, k)
188
+ if inspect.isroutine(a):
189
+ continue
190
+ self.update(k)
191
+ self.update(a)
191
192
  self.hash.update(b".")
192
193
 
193
194
  def hexdigest(self) -> str:
coverage/parser.py CHANGED
@@ -10,7 +10,6 @@ import collections
10
10
  import functools
11
11
  import os
12
12
  import re
13
- import sys
14
13
  import token
15
14
  import tokenize
16
15
  from collections.abc import Iterable, Sequence
@@ -299,10 +298,9 @@ class PythonParser:
299
298
  aaa = AstArcAnalyzer(self.filename, self._ast_root, self.raw_statements, self._multiline)
300
299
  aaa.analyze()
301
300
  arcs = aaa.arcs
302
- if env.PYBEHAVIOR.exit_through_with:
303
- self._with_jump_fixers = aaa.with_jump_fixers()
304
- if self._with_jump_fixers:
305
- arcs = self.fix_with_jumps(arcs)
301
+ self._with_jump_fixers = aaa.with_jump_fixers()
302
+ if self._with_jump_fixers:
303
+ arcs = self.fix_with_jumps(arcs)
306
304
 
307
305
  self._all_arcs = set()
308
306
  for l1, l2 in arcs:
@@ -451,33 +449,11 @@ class ByteParser:
451
449
  def _line_numbers(self) -> Iterable[TLineNo]:
452
450
  """Yield the line numbers possible in this code object.
453
451
 
454
- Uses co_lnotab described in Python/compile.c to find the
455
- line numbers. Produces a sequence: l0, l1, ...
452
+ Uses co_lines() to produce a sequence: l0, l1, ...
456
453
  """
457
- if hasattr(self.code, "co_lines"):
458
- # PYVERSIONS: new in 3.10
459
- for _, _, line in self.code.co_lines():
460
- if line:
461
- yield line
462
- else:
463
- # Adapted from dis.py in the standard library.
464
- byte_increments = self.code.co_lnotab[0::2]
465
- line_increments = self.code.co_lnotab[1::2]
466
-
467
- last_line_num: TLineNo | None = None
468
- line_num = self.code.co_firstlineno
469
- byte_num = 0
470
- for byte_incr, line_incr in zip(byte_increments, line_increments):
471
- if byte_incr:
472
- if line_num != last_line_num:
473
- yield line_num
474
- last_line_num = line_num
475
- byte_num += byte_incr
476
- if line_incr >= 0x80:
477
- line_incr -= 0x100
478
- line_num += line_incr
479
- if line_num != last_line_num:
480
- yield line_num
454
+ for _, _, line in self.code.co_lines():
455
+ if line:
456
+ yield line
481
457
 
482
458
  def _find_statements(self) -> Iterable[TLineNo]:
483
459
  """Find the statements in `self.code`.
@@ -650,19 +626,6 @@ class TryBlock(Block):
650
626
  return True
651
627
 
652
628
 
653
- class NodeList(ast.AST):
654
- """A synthetic fictitious node, containing a sequence of nodes.
655
-
656
- This is used when collapsing optimized if-statements, to represent the
657
- unconditional execution of one of the clauses.
658
-
659
- """
660
-
661
- def __init__(self, body: Sequence[ast.AST]) -> None:
662
- self.body = body
663
- self.lineno = body[0].lineno # type: ignore[attr-defined]
664
-
665
-
666
629
  # TODO: Shouldn't the cause messages join with "and" instead of "or"?
667
630
 
668
631
 
@@ -673,20 +636,22 @@ def is_constant_test_expr(node: ast.AST) -> tuple[bool, bool]:
673
636
  handle the kinds of constant expressions people might actually use.
674
637
 
675
638
  """
676
- if isinstance(node, ast.Constant):
677
- return True, bool(node.value)
678
- elif isinstance(node, ast.Name):
679
- if node.id in ["True", "False", "None", "__debug__"]:
680
- return True, eval(node.id) # pylint: disable=eval-used
681
- elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
682
- is_constant, val = is_constant_test_expr(node.operand)
683
- return is_constant, not val
684
- elif isinstance(node, ast.BoolOp):
685
- rets = [is_constant_test_expr(v) for v in node.values]
686
- is_constant = all(is_const for is_const, _ in rets)
687
- if is_constant:
688
- op = any if isinstance(node.op, ast.Or) else all
689
- return True, op(v for _, v in rets)
639
+ match node:
640
+ case ast.Constant():
641
+ return True, bool(node.value)
642
+ case ast.Name():
643
+ if node.id in ["True", "False", "None", "__debug__"]:
644
+ return True, eval(node.id) # pylint: disable=eval-used
645
+ case ast.UnaryOp():
646
+ if isinstance(node.op, ast.Not):
647
+ is_constant, val = is_constant_test_expr(node.operand)
648
+ return is_constant, not val
649
+ case ast.BoolOp():
650
+ rets = [is_constant_test_expr(v) for v in node.values]
651
+ is_constant = all(is_const for is_const, _ in rets)
652
+ if is_constant:
653
+ op = any if isinstance(node.op, ast.Or) else all
654
+ return True, op(v for _, v in rets)
690
655
  return False, False
691
656
 
692
657
 
@@ -890,14 +855,8 @@ class AstArcAnalyzer:
890
855
  else:
891
856
  return node.lineno
892
857
 
893
- def _line__Module(self, node: ast.Module) -> TLineNo:
894
- if env.PYBEHAVIOR.module_firstline_1:
895
- return 1
896
- elif node.body:
897
- return self.line_for_node(node.body[0])
898
- else:
899
- # Empty modules have no line number, they always start at 1.
900
- return 1
858
+ def _line__Module(self, node: ast.Module) -> TLineNo: # pylint: disable=unused-argument
859
+ return 1
901
860
 
902
861
  # The node types that just flow to the next node with no complications.
903
862
  OK_TO_DEFAULT = {
@@ -982,98 +941,12 @@ class AstArcAnalyzer:
982
941
  for body_node in body:
983
942
  lineno = self.line_for_node(body_node)
984
943
  if lineno not in self.statements:
985
- maybe_body_node = self.find_non_missing_node(body_node)
986
- if maybe_body_node is None:
987
- continue
988
- body_node = maybe_body_node
989
- lineno = self.line_for_node(body_node)
944
+ continue
990
945
  for prev_start in prev_starts:
991
946
  self.add_arc(prev_start.lineno, lineno, prev_start.cause)
992
947
  prev_starts = self.node_exits(body_node)
993
948
  return prev_starts
994
949
 
995
- def find_non_missing_node(self, node: ast.AST) -> ast.AST | None:
996
- """Search `node` looking for a child that has not been optimized away.
997
-
998
- This might return the node you started with, or it will work recursively
999
- to find a child node in self.statements.
1000
-
1001
- Returns a node, or None if none of the node remains.
1002
-
1003
- """
1004
- # This repeats work just done in process_body, but this duplication
1005
- # means we can avoid a function call in the 99.9999% case of not
1006
- # optimizing away statements.
1007
- lineno = self.line_for_node(node)
1008
- if lineno in self.statements:
1009
- return node
1010
-
1011
- missing_fn = cast(
1012
- Optional[Callable[[ast.AST], Optional[ast.AST]]],
1013
- getattr(self, f"_missing__{node.__class__.__name__}", None),
1014
- )
1015
- if missing_fn is not None:
1016
- ret_node = missing_fn(node)
1017
- else:
1018
- ret_node = None
1019
- return ret_node
1020
-
1021
- # Missing nodes: _missing__*
1022
- #
1023
- # Entire statements can be optimized away by Python. They will appear in
1024
- # the AST, but not the bytecode. These functions are called (by
1025
- # find_non_missing_node) to find a node to use instead of the missing
1026
- # node. They can return None if the node should truly be gone.
1027
-
1028
- def _missing__If(self, node: ast.If) -> ast.AST | None:
1029
- # If the if-node is missing, then one of its children might still be
1030
- # here, but not both. So return the first of the two that isn't missing.
1031
- # Use a NodeList to hold the clauses as a single node.
1032
- non_missing = self.find_non_missing_node(NodeList(node.body))
1033
- if non_missing:
1034
- return non_missing
1035
- if node.orelse:
1036
- return self.find_non_missing_node(NodeList(node.orelse))
1037
- return None
1038
-
1039
- def _missing__NodeList(self, node: NodeList) -> ast.AST | None:
1040
- # A NodeList might be a mixture of missing and present nodes. Find the
1041
- # ones that are present.
1042
- non_missing_children = []
1043
- for child in node.body:
1044
- maybe_child = self.find_non_missing_node(child)
1045
- if maybe_child is not None:
1046
- non_missing_children.append(maybe_child)
1047
-
1048
- # Return the simplest representation of the present children.
1049
- if not non_missing_children:
1050
- return None
1051
- if len(non_missing_children) == 1:
1052
- return non_missing_children[0]
1053
- return NodeList(non_missing_children)
1054
-
1055
- def _missing__While(self, node: ast.While) -> ast.AST | None:
1056
- body_nodes = self.find_non_missing_node(NodeList(node.body))
1057
- if not body_nodes:
1058
- return None
1059
- # Make a synthetic While-true node.
1060
- new_while = ast.While() # type: ignore[call-arg]
1061
- new_while.lineno = body_nodes.lineno # type: ignore[attr-defined]
1062
- new_while.test = ast.Name() # type: ignore[call-arg]
1063
- new_while.test.lineno = body_nodes.lineno # type: ignore[attr-defined]
1064
- new_while.test.id = "True"
1065
- assert hasattr(body_nodes, "body")
1066
- new_while.body = body_nodes.body
1067
- new_while.orelse = []
1068
- return new_while
1069
-
1070
- # In the fullness of time, these might be good tests to write:
1071
- # while EXPR:
1072
- # while False:
1073
- # listcomps hidden deep in other expressions
1074
- # listcomps hidden in lists: x = [[i for i in range(10)]]
1075
- # nested function definitions
1076
-
1077
950
  # Exit processing: process_*_exits
1078
951
  #
1079
952
  # These functions process the four kinds of jump exits: break, continue,
@@ -1192,41 +1065,34 @@ class AstArcAnalyzer:
1192
1065
  exits |= self.process_body(node.orelse, from_start=from_start)
1193
1066
  return exits
1194
1067
 
1195
- if sys.version_info >= (3, 10):
1196
-
1197
- def _handle__Match(self, node: ast.Match) -> set[ArcStart]:
1198
- start = self.line_for_node(node)
1199
- last_start = start
1200
- exits = set()
1201
- for case in node.cases:
1202
- case_start = self.line_for_node(case.pattern)
1203
- self.add_arc(last_start, case_start, "the pattern on line {lineno} always matched")
1204
- from_start = ArcStart(
1205
- case_start,
1206
- cause="the pattern on line {lineno} never matched",
1207
- )
1208
- exits |= self.process_body(case.body, from_start=from_start)
1209
- last_start = case_start
1210
-
1211
- # case is now the last case, check for wildcard match.
1212
- pattern = case.pattern # pylint: disable=undefined-loop-variable
1213
- while isinstance(pattern, ast.MatchOr):
1214
- pattern = pattern.patterns[-1]
1215
- while isinstance(pattern, ast.MatchAs) and pattern.pattern is not None:
1216
- pattern = pattern.pattern
1217
- had_wildcard = (
1218
- isinstance(pattern, ast.MatchAs) and pattern.pattern is None and case.guard is None # pylint: disable=undefined-loop-variable
1068
+ def _handle__Match(self, node: ast.Match) -> set[ArcStart]:
1069
+ start = self.line_for_node(node)
1070
+ last_start = start
1071
+ exits = set()
1072
+ for case in node.cases:
1073
+ case_start = self.line_for_node(case.pattern)
1074
+ self.add_arc(last_start, case_start, "the pattern on line {lineno} always matched")
1075
+ from_start = ArcStart(
1076
+ case_start,
1077
+ cause="the pattern on line {lineno} never matched",
1219
1078
  )
1079
+ exits |= self.process_body(case.body, from_start=from_start)
1080
+ last_start = case_start
1081
+
1082
+ # case is now the last case, check for wildcard match.
1083
+ pattern = case.pattern # pylint: disable=undefined-loop-variable
1084
+ while isinstance(pattern, ast.MatchOr):
1085
+ pattern = pattern.patterns[-1]
1086
+ while isinstance(pattern, ast.MatchAs) and pattern.pattern is not None:
1087
+ pattern = pattern.pattern
1088
+ had_wildcard = (
1089
+ isinstance(pattern, ast.MatchAs) and pattern.pattern is None and case.guard is None # pylint: disable=undefined-loop-variable
1090
+ )
1220
1091
 
1221
- if not had_wildcard:
1222
- exits.add(
1223
- ArcStart(case_start, cause="the pattern on line {lineno} always matched"),
1224
- )
1225
- return exits
1226
-
1227
- def _handle__NodeList(self, node: NodeList) -> set[ArcStart]:
1228
- start = self.line_for_node(node)
1229
- exits = self.process_body(node.body, from_start=ArcStart(start))
1092
+ if not had_wildcard:
1093
+ exits.add(
1094
+ ArcStart(case_start, cause="the pattern on line {lineno} always matched"),
1095
+ )
1230
1096
  return exits
1231
1097
 
1232
1098
  def _handle__Raise(self, node: ast.Raise) -> set[ArcStart]:
@@ -1301,13 +1167,6 @@ class AstArcAnalyzer:
1301
1167
  def _handle__While(self, node: ast.While) -> set[ArcStart]:
1302
1168
  start = to_top = self.line_for_node(node.test)
1303
1169
  constant_test, _ = is_constant_test_expr(node.test)
1304
- top_is_body0 = False
1305
- if constant_test:
1306
- top_is_body0 = True
1307
- if env.PYBEHAVIOR.keep_constant_test:
1308
- top_is_body0 = False
1309
- if top_is_body0:
1310
- to_top = self.line_for_node(node.body[0])
1311
1170
  self.block_stack.append(LoopBlock(start=to_top))
1312
1171
  from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
1313
1172
  exits = self.process_body(node.body, from_start=from_start)
@@ -1332,22 +1191,20 @@ class AstArcAnalyzer:
1332
1191
  starts = [self.line_for_node(item.context_expr) for item in node.items]
1333
1192
  else:
1334
1193
  starts = [self.line_for_node(node)]
1335
- if env.PYBEHAVIOR.exit_through_with:
1336
- for start in starts:
1337
- self.current_with_starts.add(start)
1338
- self.all_with_starts.add(start)
1194
+ for start in starts:
1195
+ self.current_with_starts.add(start)
1196
+ self.all_with_starts.add(start)
1339
1197
 
1340
1198
  exits = self.process_body(node.body, from_start=ArcStart(starts[-1]))
1341
1199
 
1342
- if env.PYBEHAVIOR.exit_through_with:
1343
- start = starts[-1]
1344
- self.current_with_starts.remove(start)
1345
- with_exit = {ArcStart(start)}
1346
- if exits:
1347
- for xit in exits:
1348
- self.add_arc(xit.lineno, start)
1349
- self.with_exits.add((xit.lineno, start))
1350
- exits = with_exit
1200
+ start = starts[-1]
1201
+ self.current_with_starts.remove(start)
1202
+ with_exit = {ArcStart(start)}
1203
+ if exits:
1204
+ for xit in exits:
1205
+ self.add_arc(xit.lineno, start)
1206
+ self.with_exits.add((xit.lineno, start))
1207
+ exits = with_exit
1351
1208
 
1352
1209
  return exits
1353
1210
 
coverage/patch.py CHANGED
@@ -32,20 +32,21 @@ def apply_patches(
32
32
  """Apply invasive patches requested by `[run] patch=`."""
33
33
  debug = debug if debug.should("patch") else DevNullDebug()
34
34
  for patch in sorted(set(config.patch)):
35
- if patch == "_exit":
36
- _patch__exit(cov, debug)
35
+ match patch:
36
+ case "_exit":
37
+ _patch__exit(cov, debug)
37
38
 
38
- elif patch == "execv":
39
- _patch_execv(cov, config, debug)
39
+ case "execv":
40
+ _patch_execv(cov, config, debug)
40
41
 
41
- elif patch == "fork":
42
- _patch_fork(debug)
42
+ case "fork":
43
+ _patch_fork(debug)
43
44
 
44
- elif patch == "subprocess":
45
- _patch_subprocess(config, debug, make_pth_file)
45
+ case "subprocess":
46
+ _patch_subprocess(config, debug, make_pth_file)
46
47
 
47
- else:
48
- raise ConfigError(f"Unknown patch {patch!r}")
48
+ case _:
49
+ raise ConfigError(f"Unknown patch {patch!r}")
49
50
 
50
51
 
51
52
  def _patch__exit(cov: Coverage, debug: TDebugCtl) -> None:
coverage/phystokens.py CHANGED
@@ -95,7 +95,7 @@ def find_soft_key_lines(source: str) -> set[TLineNo]:
95
95
  soft_key_lines: set[TLineNo] = set()
96
96
 
97
97
  for node in ast.walk(ast.parse(source)):
98
- if sys.version_info >= (3, 10) and isinstance(node, ast.Match):
98
+ if isinstance(node, ast.Match):
99
99
  soft_key_lines.add(node.lineno)
100
100
  for case in node.cases:
101
101
  soft_key_lines.add(case.pattern.lineno)
@@ -128,10 +128,7 @@ def source_token_lines(source: str) -> TSourceTokenLines:
128
128
  source = source.expandtabs(8).replace("\r\n", "\n")
129
129
  tokgen = generate_tokens(source)
130
130
 
131
- if env.PYBEHAVIOR.soft_keywords:
132
- soft_key_lines = find_soft_key_lines(source)
133
- else:
134
- soft_key_lines = set()
131
+ soft_key_lines = find_soft_key_lines(source)
135
132
 
136
133
  for ttype, ttext, (sline, scol), (_, ecol), _ in _phys_tokens(tokgen):
137
134
  mark_start = True
@@ -157,7 +154,7 @@ def source_token_lines(source: str) -> TSourceTokenLines:
157
154
  if keyword.iskeyword(ttext):
158
155
  # Hard keywords are always keywords.
159
156
  tok_class = "key"
160
- elif env.PYBEHAVIOR.soft_keywords and keyword.issoftkeyword(ttext):
157
+ elif keyword.issoftkeyword(ttext):
161
158
  # Soft keywords appear at the start of their line.
162
159
  if len(line) == 0:
163
160
  is_start_of_line = True
Binary file
coverage/types.py CHANGED
@@ -11,7 +11,7 @@ import os
11
11
  import pathlib
12
12
  from collections.abc import Iterable, Mapping
13
13
  from types import FrameType, ModuleType
14
- from typing import TYPE_CHECKING, Any, Callable, Optional, Protocol, Union
14
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Protocol
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from coverage.plugin import FileTracer
@@ -22,14 +22,10 @@ AnyCallable = Callable[..., Any]
22
22
  ## File paths
23
23
 
24
24
  # For arguments that are file paths:
25
- if TYPE_CHECKING:
26
- FilePath = Union[str, os.PathLike[str]]
27
- else:
28
- # PathLike < python3.9 doesn't support subscription
29
- FilePath = Union[str, os.PathLike]
25
+ FilePath = str | os.PathLike[str]
30
26
  # For testing FilePath arguments
31
27
  FilePathClasses = [str, pathlib.Path]
32
- FilePathType = Union[type[str], type[pathlib.Path]]
28
+ FilePathType = type[str] | type[pathlib.Path]
33
29
 
34
30
  ## Python tracing
35
31
 
@@ -77,14 +73,14 @@ class TFileDisposition(Protocol):
77
73
  # - If measuring arcs in the C tracer, the values are sets of packed arcs (two
78
74
  # line numbers combined into one integer).
79
75
 
80
- TTraceFileData = Union[set[TLineNo], set[TArc], set[int]]
76
+ TTraceFileData = set[TLineNo] | set[TArc] | set[int]
81
77
 
82
78
  TTraceData = dict[str, TTraceFileData]
83
79
 
84
80
  # Functions passed into collectors.
85
81
  TShouldTraceFn = Callable[[str, FrameType], TFileDisposition]
86
82
  TCheckIncludeFn = Callable[[str, FrameType], bool]
87
- TShouldStartContextFn = Callable[[FrameType], Union[str, None]]
83
+ TShouldStartContextFn = Callable[[FrameType], str | None]
88
84
 
89
85
 
90
86
  class Tracer(Protocol):
@@ -127,8 +123,8 @@ TCovKwargs = Any
127
123
  ## Configuration
128
124
 
129
125
  # One value read from a config file.
130
- TConfigValueIn = Optional[Union[bool, int, float, str, Iterable[str], Mapping[str, Iterable[str]]]]
131
- TConfigValueOut = Optional[Union[bool, int, float, str, list[str], dict[str, list[str]]]]
126
+ TConfigValueIn = Optional[bool | int | float | str | Iterable[str] | Mapping[str, Iterable[str]]]
127
+ TConfigValueOut = Optional[bool | int | float | str | list[str] | dict[str, list[str]]]
132
128
  # An entire config section, mapping option names to values.
133
129
  TConfigSectionIn = Mapping[str, TConfigValueIn]
134
130
  TConfigSectionOut = Mapping[str, TConfigValueOut]
@@ -169,7 +165,7 @@ class TPluginConfig(Protocol):
169
165
 
170
166
  ## Parsing
171
167
 
172
- TMorf = Union[ModuleType, str]
168
+ TMorf = ModuleType | str
173
169
 
174
170
  TSourceTokenLines = Iterable[list[tuple[str, str]]]
175
171
 
coverage/version.py CHANGED
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  # version_info: same semantics as sys.version_info.
10
10
  # _dev: the .devN suffix if any.
11
- version_info = (7, 10, 7, "final", 0)
11
+ version_info = (7, 11, 0, "final", 0)
12
12
  _dev = 0
13
13
 
14
14
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coverage
3
- Version: 7.10.7
3
+ Version: 7.11.0
4
4
  Summary: Code coverage measurement for Python
5
5
  Home-page: https://github.com/nedbat/coveragepy
6
6
  Author: Ned Batchelder and 249 others
7
7
  Author-email: ned@nedbatchelder.com
8
8
  License: Apache-2.0
9
- Project-URL: Documentation, https://coverage.readthedocs.io/en/7.10.7
9
+ Project-URL: Documentation, https://coverage.readthedocs.io/en/7.11.0
10
10
  Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi
11
11
  Project-URL: Issues, https://github.com/nedbat/coveragepy/issues
12
12
  Project-URL: Mastodon, https://hachyderm.io/@coveragepy
@@ -17,18 +17,18 @@ Classifier: Intended Audience :: Developers
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python
19
19
  Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
23
22
  Classifier: Programming Language :: Python :: 3.12
24
23
  Classifier: Programming Language :: Python :: 3.13
25
24
  Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Programming Language :: Python :: 3.15
26
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
27
27
  Classifier: Programming Language :: Python :: Implementation :: PyPy
28
28
  Classifier: Topic :: Software Development :: Quality Assurance
29
29
  Classifier: Topic :: Software Development :: Testing
30
30
  Classifier: Development Status :: 5 - Production/Stable
31
- Requires-Python: >=3.9
31
+ Requires-Python: >=3.10
32
32
  Description-Content-Type: text/x-rst
33
33
  License-File: LICENSE.txt
34
34
  Provides-Extra: toml
@@ -75,13 +75,13 @@ Coverage.py runs on these versions of Python:
75
75
 
76
76
  .. PYVERSIONS
77
77
 
78
- * Python 3.9 through 3.14 rc2, including free-threading.
79
- * PyPy3 versions 3.9, 3.10, and 3.11.
78
+ * Python 3.10 through 3.15 alpha, including free-threading.
79
+ * PyPy3 versions 3.10 and 3.11.
80
80
 
81
81
  Documentation is on `Read the Docs`_. Code repository and issue tracker are on
82
82
  `GitHub`_.
83
83
 
84
- .. _Read the Docs: https://coverage.readthedocs.io/en/7.10.7/
84
+ .. _Read the Docs: https://coverage.readthedocs.io/en/7.11.0/
85
85
  .. _GitHub: https://github.com/nedbat/coveragepy
86
86
 
87
87
  **New in 7.x:**
@@ -93,7 +93,7 @@ Documentation is on `Read the Docs`_. Code repository and issue tracker are on
93
93
  multi-line exclusion patterns;
94
94
  function/class reporting;
95
95
  experimental support for sys.monitoring;
96
- dropped support for Python 3.7 and 3.8;
96
+ dropped support for Python up to 3.9;
97
97
  added ``Coverage.collect()`` context manager;
98
98
  improved data combining;
99
99
  ``[run] exclude_also`` setting;
@@ -131,7 +131,7 @@ Getting Started
131
131
  Looking to run ``coverage`` on your test suite? See the `Quick Start section`_
132
132
  of the docs.
133
133
 
134
- .. _Quick Start section: https://coverage.readthedocs.io/en/7.10.7/#quick-start
134
+ .. _Quick Start section: https://coverage.readthedocs.io/en/7.11.0/#quick-start
135
135
 
136
136
 
137
137
  Change history
@@ -139,7 +139,7 @@ Change history
139
139
 
140
140
  The complete history of changes is on the `change history page`_.
141
141
 
142
- .. _change history page: https://coverage.readthedocs.io/en/7.10.7/changes.html
142
+ .. _change history page: https://coverage.readthedocs.io/en/7.11.0/changes.html
143
143
 
144
144
 
145
145
  Code of Conduct
@@ -158,7 +158,7 @@ Contributing
158
158
  Found a bug? Want to help improve the code or documentation? See the
159
159
  `Contributing section`_ of the docs.
160
160
 
161
- .. _Contributing section: https://coverage.readthedocs.io/en/7.10.7/contributing.html
161
+ .. _Contributing section: https://coverage.readthedocs.io/en/7.11.0/contributing.html
162
162
 
163
163
 
164
164
  Security
@@ -186,7 +186,7 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
186
186
  :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
187
187
  :alt: Quality check status
188
188
  .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
189
- :target: https://coverage.readthedocs.io/en/7.10.7/
189
+ :target: https://coverage.readthedocs.io/en/7.11.0/
190
190
  :alt: Documentation
191
191
  .. |kit| image:: https://img.shields.io/pypi/v/coverage
192
192
  :target: https://pypi.org/project/coverage/
@@ -195,7 +195,7 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
195
195
  :target: https://pypi.org/project/coverage/
196
196
  :alt: Python versions supported
197
197
  .. |license| image:: https://img.shields.io/pypi/l/coverage.svg
198
- :target: https://pypi.org/project/coverage/
198
+ :target: https://github.com/nedbat/coveragepy/blob/master/LICENSE.txt
199
199
  :alt: License
200
200
  .. |metacov| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nedbat/8c6980f77988a327348f9b02bbaf67f5/raw/metacov.json
201
201
  :target: https://nedbat.github.io/coverage-reports/latest.html
@@ -2,29 +2,29 @@ coverage/__init__.py,sha256=p80cmYrM35VlYk07TtKV90tz0FyOH7HFS_mMqqlkQO8,1103
2
2
  coverage/__main__.py,sha256=M2jcqCZIu_rkmoO4CKgwElX084u6SHdZfVUg3lGiWhg,307
3
3
  coverage/annotate.py,sha256=rcUxalBphRmZePMoSZO8yPCMR7NkdEKsglUmAQV7COM,3863
4
4
  coverage/bytecode.py,sha256=eaUA9uzyxoC_T11d4hdxZyRZVhtbNRDUsVlA4_sQ1rQ,6504
5
- coverage/cmdline.py,sha256=CDTLIuJJoIGf_NxYl11JJYxmVj5tf8FLLGPFqkCwmBo,37762
6
- coverage/collector.py,sha256=RG7gLhzGAu4ZCyLx5XhQEsDKdVxeSGq5uhhybhVislk,19902
7
- coverage/config.py,sha256=1-q4C79ea9Rv8lDu7Ra9u_1jeDHqPeOI58Efb7XsA5c,26046
5
+ coverage/cmdline.py,sha256=dCYQCAeO2Vo56W6mrW2WXdV9qy5R7v-lm0LBCPvQHYk,37752
6
+ coverage/collector.py,sha256=JEnp6NKbywHOopu6FXlNDlnk2NbbOqa4kNpx1FoR2zw,19798
7
+ coverage/config.py,sha256=JL7XwK4uoW5wVMjT-hU6cFpgHCimUtUiAEQzSNaSOO4,26033
8
8
  coverage/context.py,sha256=YX7Pg0y3Og-d3qDcgoMmEpuf9jIcigcYnF0edZaqIsM,2506
9
- coverage/control.py,sha256=MR8t7lPGctYRbws-GUKt8qq77pfuzRDx-JKIVg6BMP8,56100
9
+ coverage/control.py,sha256=oPZYE10LjV5gJtTEVrhb5hAARXO5DkFJLbxpZu-AF90,56087
10
10
  coverage/core.py,sha256=FgNbMrDRj9_ETraUQLjx3baRaUnpmb4CAW32S1DCUqg,4477
11
11
  coverage/data.py,sha256=WuNUTfXoytulNWHA-F1OnSfvqu0DgSN7tg9R4ow2CjA,8352
12
- coverage/debug.py,sha256=rSJ9rbZzNj6KIZslUR3VQ2YUBTfWdK7aG3cgH87d_1U,22207
12
+ coverage/debug.py,sha256=rY7n6UYRtIaWjC1Wn764AfH8XjYrvJB3XZcvQAH-bNs,22235
13
13
  coverage/disposition.py,sha256=eZmBzTPMmLkDi5OLRNrqtJQMZ4XXh4sHrP1xPEZ50D8,1954
14
- coverage/env.py,sha256=sbWgpZKO8xzXtQP6_RRPSCEjMFBbDUc5A3y1tqfQW8s,7610
15
- coverage/exceptions.py,sha256=tSV5ajKEhCEy4G-ZGMy1sxI-sJG26HMVwjJyJzirX-0,1631
14
+ coverage/env.py,sha256=-QCJcrE6UoJmUzO_3shtviXViblPiYEBEmb6vgrpq0c,5105
15
+ coverage/exceptions.py,sha256=SM3t24dubPw7bB1oAjo2t3M2B-kaJWBudoYsuGX-v4c,1502
16
16
  coverage/execfile.py,sha256=TSqB02-3aCYLqPe_ELpD91Y29UVi7EwOLrkTZsZ4pFg,12320
17
17
  coverage/files.py,sha256=xXp40_DeEM8oUd-EEtUcvK24YHTxmiHCfhfcgeOTQxc,19908
18
18
  coverage/html.py,sha256=9oO7s6BHzoHNaYCldJ_k9oFD4TIMDd0x4wH_jXJaSUw,32238
19
19
  coverage/inorout.py,sha256=HbNb7xIolXGyXtohzwgDQCLrO4HkFnGo5oCvws2lqmY,24957
20
20
  coverage/jsonreport.py,sha256=wZgntJY2uApDvAdpnNPcrwNvmyb3Tc4XK3fN5L2Opf0,7255
21
21
  coverage/lcovreport.py,sha256=OmqTh9rQ2GaIRYADx_3OOhmfZOoG_ocPqbOB9j7Kp_A,8091
22
- coverage/misc.py,sha256=xytcM5j7NDwhWPB8jdWMwqmiEy8fImMLUZExm1eSPOw,11608
22
+ coverage/misc.py,sha256=pvDaKapNAHzE76UaHRqsJuhhmMhgXBJLAYKwEIOwPkI,11662
23
23
  coverage/multiproc.py,sha256=PpThtQg0j9NHlYpceNxx5wg9O4XdOjjt03jlI2rkfwI,4293
24
24
  coverage/numbits.py,sha256=t7XK_d-I-tVZgRS2M3Y5PG0Pq017D3rvecSipBsT_CA,4817
25
- coverage/parser.py,sha256=oQDSOwjbyYxpf-6LZhFFmum1WmgKrwz1S-WCkPnnmN8,53500
26
- coverage/patch.py,sha256=tcw8V58zIgDdm-cyMmgtAjNaHhOZQkuJuYmIQ91fD_M,5701
27
- coverage/phystokens.py,sha256=enBRdptUw3gxDGLfgrxqtCR0gTRQzJU-AkHZU-pd4uA,7795
25
+ coverage/parser.py,sha256=vP552qMlBqw-uUwA8Vbf8Cv4W37CxP8BH-3owuojqHg,47437
26
+ coverage/patch.py,sha256=3GHFJnl1scWtvRVhFp2-qPHdAcF8Ve44vdl8xl3C7Oo,5731
27
+ coverage/phystokens.py,sha256=QYlZQBB343TW7SRHhaeo6AoKr9abhMCFjSC4c6aLg3Y,7645
28
28
  coverage/plugin.py,sha256=sWFPqm3JrMLRrRNZryP3WmRi3VbGBmCWol9cKEKFYas,22124
29
29
  coverage/plugin_support.py,sha256=DWcHXg2Gde0r5XH68sxygXpLGcMlp8H6zDhNOKg6Wc4,10741
30
30
  coverage/py.typed,sha256=QhKiyYY18iX2PTjtgwSYebGpYvH_m0bYEyU_kMST7EU,73
@@ -39,10 +39,10 @@ coverage/sqlitedb.py,sha256=7MmRxX7alrq-Elzx5TzJJNi6xURwUH5mX2Pvgr_Qnp8,10249
39
39
  coverage/sysmon.py,sha256=u3Oek5ToXFSwjy_x9zOymOWFznRFPT0YxIXyou_QU_Y,17965
40
40
  coverage/templite.py,sha256=ETQ1TTufhLF6qlS2kuaX9gg7iQ-c0V6hTlGcInJ0dWA,11116
41
41
  coverage/tomlconfig.py,sha256=GtiVV3RzKwj5Z6-RSL8XZFSO5iLfj97dcvqpODkw5Ek,7766
42
- coverage/tracer.cp314-win_arm64.pyd,sha256=_i2rNsoC_VxS25ka8AaMSeJA4SMHLM6nbRFgleqpFpk,22016
42
+ coverage/tracer.cp314-win_arm64.pyd,sha256=4cRG-ida0S1tKIWE_U0YdeQdUf1AkOgxRQhm4Jym5Pw,22016
43
43
  coverage/tracer.pyi,sha256=e1YXvQg8IuQaaoP7Tj25It3pNAtv0Mt29DwwxO4KfaM,1248
44
- coverage/types.py,sha256=dUQ91LEjNe3e4WHI1bzNYdTMKy_axXgVPU84mPzFMp0,5971
45
- coverage/version.py,sha256=pERk5JGxocE8y6JQWede_XNJx6IKyUzeF_PJBsZFYZE,1127
44
+ coverage/types.py,sha256=0f03sJRwr4TZItHwZPHNFxL5bhjU1B3ye8C1BVQaUPc,5804
45
+ coverage/version.py,sha256=S45hpB6zMbJnEW8jDIl4pxGE0QjLTKTOJvk5DUXNmhc,1127
46
46
  coverage/xmlreport.py,sha256=Wha1LxJgIEqKPO2hVqywTDt3QfPNtk4bgHqxBnog-mA,10133
47
47
  coverage/htmlfiles/coverage_html.js,sha256=PqDTAlVdIaB9gIjEf6JHHysMm_D7HyRe4BzQFfpf3OM,26207
48
48
  coverage/htmlfiles/favicon_32.png,sha256=vIEA-odDwRvSQ-syWfSwEnWGUWEv2b-Tv4tzTRfwJWE,1732
@@ -51,9 +51,9 @@ coverage/htmlfiles/keybd_closed.png,sha256=fZv4rmY3DkNJtPQjrFJ5UBOE5DdNof3mdeCZW
51
51
  coverage/htmlfiles/pyfile.html,sha256=dJV8bc3mMQz6J_N2KxVMXNKVge6vnMiQiqqe1QYuZmw,6643
52
52
  coverage/htmlfiles/style.css,sha256=DoE2sbDGB3s5ZrE9QCbgKivfw-HcpTRvtMeXbJn7EjA,16020
53
53
  coverage/htmlfiles/style.scss,sha256=ZjH-qCU3X7wrMfepdUhqyYc8gXaqBz6n_M9hTMU93Kw,21737
54
- coverage-7.10.7.dist-info/licenses/LICENSE.txt,sha256=6z17VIVGasvYHytJb1latjfSeS4mggayfZnnk722dUk,10351
55
- coverage-7.10.7.dist-info/METADATA,sha256=F05SGumr40ekQm0ZChZml8R46PpqE-NlcWrMn-T2wqc,9155
56
- coverage-7.10.7.dist-info/WHEEL,sha256=bMXkUWfr_3hlaRObp_pv9_U0ksvQo0T0Mwq6lQkXaZc,101
57
- coverage-7.10.7.dist-info/entry_points.txt,sha256=9yTQ78MxN2yPai5yGQH_9pCCIAbx54RIC_6FWHzS3jg,123
58
- coverage-7.10.7.dist-info/top_level.txt,sha256=BjhyiIvusb5OJkqCXjRncTF3soKF-mDOby-hxkWwwv0,9
59
- coverage-7.10.7.dist-info/RECORD,,
54
+ coverage-7.11.0.dist-info/licenses/LICENSE.txt,sha256=6z17VIVGasvYHytJb1latjfSeS4mggayfZnnk722dUk,10351
55
+ coverage-7.11.0.dist-info/METADATA,sha256=zNoryzJsdBM_IZJaCVbnOeYW3SizS9bM6TLvHBI_l4s,9178
56
+ coverage-7.11.0.dist-info/WHEEL,sha256=bMXkUWfr_3hlaRObp_pv9_U0ksvQo0T0Mwq6lQkXaZc,101
57
+ coverage-7.11.0.dist-info/entry_points.txt,sha256=9yTQ78MxN2yPai5yGQH_9pCCIAbx54RIC_6FWHzS3jg,123
58
+ coverage-7.11.0.dist-info/top_level.txt,sha256=BjhyiIvusb5OJkqCXjRncTF3soKF-mDOby-hxkWwwv0,9
59
+ coverage-7.11.0.dist-info/RECORD,,