coverage 7.12.0__cp314-cp314-musllinux_1_2_i686.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.
Files changed (59) hide show
  1. coverage/__init__.py +40 -0
  2. coverage/__main__.py +12 -0
  3. coverage/annotate.py +114 -0
  4. coverage/bytecode.py +196 -0
  5. coverage/cmdline.py +1184 -0
  6. coverage/collector.py +486 -0
  7. coverage/config.py +731 -0
  8. coverage/context.py +74 -0
  9. coverage/control.py +1481 -0
  10. coverage/core.py +139 -0
  11. coverage/data.py +227 -0
  12. coverage/debug.py +669 -0
  13. coverage/disposition.py +59 -0
  14. coverage/env.py +135 -0
  15. coverage/exceptions.py +85 -0
  16. coverage/execfile.py +329 -0
  17. coverage/files.py +553 -0
  18. coverage/html.py +860 -0
  19. coverage/htmlfiles/coverage_html.js +735 -0
  20. coverage/htmlfiles/favicon_32.png +0 -0
  21. coverage/htmlfiles/index.html +199 -0
  22. coverage/htmlfiles/keybd_closed.png +0 -0
  23. coverage/htmlfiles/pyfile.html +149 -0
  24. coverage/htmlfiles/style.css +385 -0
  25. coverage/htmlfiles/style.scss +842 -0
  26. coverage/inorout.py +614 -0
  27. coverage/jsonreport.py +192 -0
  28. coverage/lcovreport.py +219 -0
  29. coverage/misc.py +373 -0
  30. coverage/multiproc.py +120 -0
  31. coverage/numbits.py +146 -0
  32. coverage/parser.py +1215 -0
  33. coverage/patch.py +166 -0
  34. coverage/phystokens.py +197 -0
  35. coverage/plugin.py +617 -0
  36. coverage/plugin_support.py +299 -0
  37. coverage/py.typed +1 -0
  38. coverage/python.py +272 -0
  39. coverage/pytracer.py +369 -0
  40. coverage/regions.py +127 -0
  41. coverage/report.py +298 -0
  42. coverage/report_core.py +117 -0
  43. coverage/results.py +502 -0
  44. coverage/sqldata.py +1153 -0
  45. coverage/sqlitedb.py +239 -0
  46. coverage/sysmon.py +513 -0
  47. coverage/templite.py +318 -0
  48. coverage/tomlconfig.py +210 -0
  49. coverage/tracer.cpython-314-i386-linux-musl.so +0 -0
  50. coverage/tracer.pyi +43 -0
  51. coverage/types.py +206 -0
  52. coverage/version.py +35 -0
  53. coverage/xmlreport.py +264 -0
  54. coverage-7.12.0.dist-info/METADATA +221 -0
  55. coverage-7.12.0.dist-info/RECORD +59 -0
  56. coverage-7.12.0.dist-info/WHEEL +5 -0
  57. coverage-7.12.0.dist-info/entry_points.txt +4 -0
  58. coverage-7.12.0.dist-info/licenses/LICENSE.txt +177 -0
  59. coverage-7.12.0.dist-info/top_level.txt +1 -0
coverage/plugin.py ADDED
@@ -0,0 +1,617 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
3
+
4
+ """
5
+ .. versionadded:: 4.0
6
+
7
+ Plug-in interfaces for coverage.py.
8
+
9
+ Coverage.py supports a few different kinds of plug-ins that change its
10
+ behavior:
11
+
12
+ * File tracers implement tracing of non-Python file types.
13
+
14
+ * Configurers add custom configuration, using Python code to change the
15
+ configuration.
16
+
17
+ * Dynamic context switchers decide when the dynamic context has changed, for
18
+ example, to record what test function produced the coverage.
19
+
20
+ To write a coverage.py plug-in, create a module with a subclass of
21
+ :class:`~coverage.CoveragePlugin`. You will override methods in your class to
22
+ participate in various aspects of coverage.py's processing.
23
+ Different types of plug-ins have to override different methods.
24
+
25
+ Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
26
+ to provide debugging information about their operation.
27
+
28
+ Your module must also contain a ``coverage_init`` function that registers an
29
+ instance of your plug-in class::
30
+
31
+ import coverage
32
+
33
+ class MyPlugin(coverage.CoveragePlugin):
34
+ ...
35
+
36
+ def coverage_init(reg, options):
37
+ reg.add_file_tracer(MyPlugin())
38
+
39
+ You use the `reg` parameter passed to your ``coverage_init`` function to
40
+ register your plug-in object. The registration method you call depends on
41
+ what kind of plug-in it is.
42
+
43
+ If your plug-in takes options, the `options` parameter is a dictionary of your
44
+ plug-in's options from the coverage.py configuration file. Use them however
45
+ you want to configure your object before registering it.
46
+
47
+ Coverage.py will store its own information on your plug-in object, using
48
+ attributes whose names start with ``_coverage_``. Don't be startled.
49
+
50
+ .. warning::
51
+ Plug-ins are imported by coverage.py before it begins measuring code.
52
+ If you write a plugin in your own project, it might import your product
53
+ code before coverage.py can start measuring. This can result in your
54
+ own code being reported as missing.
55
+
56
+ One solution is to put your plugins in your project tree, but not in
57
+ your importable Python package.
58
+
59
+
60
+ .. _file_tracer_plugins:
61
+
62
+ File Tracers
63
+ ============
64
+
65
+ File tracers implement measurement support for non-Python files. File tracers
66
+ implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
67
+ files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
68
+ on those files.
69
+
70
+ In your ``coverage_init`` function, use the ``add_file_tracer`` method to
71
+ register your file tracer.
72
+
73
+
74
+ .. _configurer_plugins:
75
+
76
+ Configurers
77
+ ===========
78
+
79
+ .. versionadded:: 4.5
80
+
81
+ Configurers modify the configuration of coverage.py during start-up.
82
+ Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
83
+ change the configuration.
84
+
85
+ In your ``coverage_init`` function, use the ``add_configurer`` method to
86
+ register your configurer.
87
+
88
+
89
+ .. _dynamic_context_plugins:
90
+
91
+ Dynamic Context Switchers
92
+ =========================
93
+
94
+ .. versionadded:: 5.0
95
+
96
+ Dynamic context switcher plugins implement the
97
+ :meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute
98
+ the context label for each measured frame.
99
+
100
+ Computed context labels are useful when you want to group measured data without
101
+ modifying the source code.
102
+
103
+ For example, you could write a plugin that checks `frame.f_code` to inspect
104
+ the currently executed method, and set the context label to a fully qualified
105
+ method name if it's an instance method of `unittest.TestCase` and the method
106
+ name starts with 'test'. Such a plugin would provide basic coverage grouping
107
+ by test and could be used with test runners that have no built-in coveragepy
108
+ support.
109
+
110
+ In your ``coverage_init`` function, use the ``add_dynamic_context`` method to
111
+ register your dynamic context switcher.
112
+
113
+ """
114
+
115
+ from __future__ import annotations
116
+
117
+ import dataclasses
118
+ import functools
119
+ from collections.abc import Iterable
120
+ from types import FrameType
121
+ from typing import Any
122
+
123
+ from coverage import files
124
+ from coverage.misc import _needs_to_implement
125
+ from coverage.types import TArc, TConfigurable, TLineNo, TSourceTokenLines
126
+
127
+
128
+ class CoveragePlugin:
129
+ """Base class for coverage.py plug-ins."""
130
+
131
+ _coverage_plugin_name: str
132
+ _coverage_enabled: bool
133
+
134
+ def file_tracer(self, filename: str) -> FileTracer | None: # pylint: disable=unused-argument
135
+ """Get a :class:`FileTracer` object for a file.
136
+
137
+ Plug-in type: file tracer.
138
+
139
+ Every Python source file is offered to your plug-in to give it a chance
140
+ to take responsibility for tracing the file. If your plug-in can
141
+ handle the file, it should return a :class:`FileTracer` object.
142
+ Otherwise return None.
143
+
144
+ There is no way to register your plug-in for particular files.
145
+ Instead, this method is invoked for all files as they are executed,
146
+ and the plug-in decides whether it can trace the file or not.
147
+ Be prepared for `filename` to refer to all kinds of files that have
148
+ nothing to do with your plug-in.
149
+
150
+ The file name will be a Python file being executed. There are two
151
+ broad categories of behavior for a plug-in, depending on the kind of
152
+ files your plug-in supports:
153
+
154
+ * Static file names: each of your original source files has been
155
+ converted into a distinct Python file. Your plug-in is invoked with
156
+ the Python file name, and it maps it back to its original source
157
+ file.
158
+
159
+ * Dynamic file names: all of your source files are executed by the same
160
+ Python file. In this case, your plug-in implements
161
+ :meth:`FileTracer.dynamic_source_filename` to provide the actual
162
+ source file for each execution frame.
163
+
164
+ `filename` is a string, the path to the file being considered. This is
165
+ the absolute real path to the file. If you are comparing to other
166
+ paths, be sure to take this into account.
167
+
168
+ Returns a :class:`FileTracer` object to use to trace `filename`, or
169
+ None if this plug-in cannot trace this file.
170
+
171
+ """
172
+ return None
173
+
174
+ def file_reporter(
175
+ self,
176
+ filename: str, # pylint: disable=unused-argument
177
+ ) -> FileReporter | str: # str should be Literal["python"]
178
+ """Get the :class:`FileReporter` class to use for a file.
179
+
180
+ Plug-in type: file tracer.
181
+
182
+ This will only be invoked if `filename` returns non-None from
183
+ :meth:`file_tracer`. It's an error to return None from this method.
184
+
185
+ Returns a :class:`FileReporter` object to use to report on `filename`,
186
+ or the string `"python"` to have coverage.py treat the file as Python.
187
+
188
+ """
189
+ _needs_to_implement(self, "file_reporter")
190
+
191
+ def dynamic_context(
192
+ self,
193
+ frame: FrameType, # pylint: disable=unused-argument
194
+ ) -> str | None:
195
+ """Get the dynamically computed context label for `frame`.
196
+
197
+ Plug-in type: dynamic context.
198
+
199
+ This method is invoked for each frame when outside of a dynamic
200
+ context, to see if a new dynamic context should be started. If it
201
+ returns a string, a new context label is set for this and deeper
202
+ frames. The dynamic context ends when this frame returns.
203
+
204
+ Returns a string to start a new dynamic context, or None if no new
205
+ context should be started.
206
+
207
+ """
208
+ return None
209
+
210
+ def find_executable_files(
211
+ self,
212
+ src_dir: str, # pylint: disable=unused-argument
213
+ ) -> Iterable[str]:
214
+ """Yield all of the executable files in `src_dir`, recursively.
215
+
216
+ Plug-in type: file tracer.
217
+
218
+ Executability is a plug-in-specific property, but generally means files
219
+ which would have been considered for coverage analysis, had they been
220
+ included automatically.
221
+
222
+ Returns or yields a sequence of strings, the paths to files that could
223
+ have been executed, including files that had been executed.
224
+
225
+ """
226
+ return []
227
+
228
+ def configure(self, config: TConfigurable) -> None:
229
+ """Modify the configuration of coverage.py.
230
+
231
+ Plug-in type: configurer.
232
+
233
+ This method is called during coverage.py start-up, to give your plug-in
234
+ a chance to change the configuration. The `config` parameter is an
235
+ object with :meth:`~coverage.Coverage.get_option` and
236
+ :meth:`~coverage.Coverage.set_option` methods. Do not call any other
237
+ methods on the `config` object.
238
+
239
+ """
240
+ pass
241
+
242
+ def sys_info(self) -> Iterable[tuple[str, Any]]:
243
+ """Get a list of information useful for debugging.
244
+
245
+ Plug-in type: any.
246
+
247
+ This method will be invoked for ``--debug=sys``. Your
248
+ plug-in can return any information it wants to be displayed.
249
+
250
+ Returns a list of pairs: `[(name, value), ...]`.
251
+
252
+ """
253
+ return []
254
+
255
+
256
+ class CoveragePluginBase:
257
+ """Plugins produce specialized objects, which point back to the original plugin."""
258
+
259
+ _coverage_plugin: CoveragePlugin
260
+
261
+
262
+ class FileTracer(CoveragePluginBase):
263
+ """Support needed for files during the execution phase.
264
+
265
+ File tracer plug-ins implement subclasses of FileTracer to return from
266
+ their :meth:`~CoveragePlugin.file_tracer` method.
267
+
268
+ You may construct this object from :meth:`CoveragePlugin.file_tracer` any
269
+ way you like. A natural choice would be to pass the file name given to
270
+ `file_tracer`.
271
+
272
+ `FileTracer` objects should only be created in the
273
+ :meth:`CoveragePlugin.file_tracer` method.
274
+
275
+ See :ref:`howitworks` for details of the different coverage.py phases.
276
+
277
+ """
278
+
279
+ def source_filename(self) -> str:
280
+ """The source file name for this file.
281
+
282
+ This may be any file name you like. A key responsibility of a plug-in
283
+ is to own the mapping from Python execution back to whatever source
284
+ file name was originally the source of the code.
285
+
286
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
287
+ dynamic file names.
288
+
289
+ Returns the file name to credit with this execution.
290
+
291
+ """
292
+ _needs_to_implement(self, "source_filename")
293
+
294
+ def has_dynamic_source_filename(self) -> bool:
295
+ """Does this FileTracer have dynamic source file names?
296
+
297
+ FileTracers can provide dynamically determined file names by
298
+ implementing :meth:`dynamic_source_filename`. Invoking that function
299
+ is expensive. To determine whether to invoke it, coverage.py uses the
300
+ result of this function to know if it needs to bother invoking
301
+ :meth:`dynamic_source_filename`.
302
+
303
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
304
+ dynamic file names.
305
+
306
+ Returns True if :meth:`dynamic_source_filename` should be called to get
307
+ dynamic source file names.
308
+
309
+ """
310
+ return False
311
+
312
+ def dynamic_source_filename(
313
+ self,
314
+ filename: str, # pylint: disable=unused-argument
315
+ frame: FrameType, # pylint: disable=unused-argument
316
+ ) -> str | None:
317
+ """Get a dynamically computed source file name.
318
+
319
+ Some plug-ins need to compute the source file name dynamically for each
320
+ frame.
321
+
322
+ This function will not be invoked if
323
+ :meth:`has_dynamic_source_filename` returns False.
324
+
325
+ Returns the source file name for this frame, or None if this frame
326
+ shouldn't be measured.
327
+
328
+ """
329
+ return None
330
+
331
+ def line_number_range(self, frame: FrameType) -> tuple[TLineNo, TLineNo]:
332
+ """Get the range of source line numbers for a given a call frame.
333
+
334
+ The call frame is examined, and the source line number in the original
335
+ file is returned. The return value is a pair of numbers, the starting
336
+ line number and the ending line number, both inclusive. For example,
337
+ returning (5, 7) means that lines 5, 6, and 7 should be considered
338
+ executed.
339
+
340
+ This function might decide that the frame doesn't indicate any lines
341
+ from the source file were executed. Return (-1, -1) in this case to
342
+ tell coverage.py that no lines should be recorded for this frame.
343
+
344
+ """
345
+ lineno = frame.f_lineno
346
+ return lineno, lineno
347
+
348
+
349
+ @dataclasses.dataclass
350
+ class CodeRegion:
351
+ """Data for a region of code found by :meth:`FileReporter.code_regions`."""
352
+
353
+ #: The kind of region, like `"function"` or `"class"`. Must be one of the
354
+ #: singular values returned by :meth:`FileReporter.code_region_kinds`.
355
+ kind: str
356
+
357
+ #: The name of the region. For example, a function or class name.
358
+ name: str
359
+
360
+ #: The line in the source file to link to when navigating to the region.
361
+ #: Can be a line not mentioned in `lines`.
362
+ start: int
363
+
364
+ #: The lines in the region. Should be lines that could be executed in the
365
+ #: region. For example, a class region includes all of the lines in the
366
+ #: methods of the class, but not the lines defining class attributes, since
367
+ #: they are executed on import, not as part of exercising the class. The
368
+ #: set can include non-executable lines like blanks and comments.
369
+ lines: set[int]
370
+
371
+ def __lt__(self, other: CodeRegion) -> bool:
372
+ """To support sorting to make test-writing easier."""
373
+ if self.name == other.name:
374
+ return min(self.lines) < min(other.lines)
375
+ return self.name < other.name
376
+
377
+
378
+ @functools.total_ordering
379
+ class FileReporter(CoveragePluginBase):
380
+ """Support needed for files during the analysis and reporting phases.
381
+
382
+ File tracer plug-ins implement a subclass of `FileReporter`, and return
383
+ instances from their :meth:`CoveragePlugin.file_reporter` method.
384
+
385
+ There are many methods here, but only :meth:`lines` is required, to provide
386
+ the set of executable lines in the file.
387
+
388
+ See :ref:`howitworks` for details of the different coverage.py phases.
389
+
390
+ """
391
+
392
+ def __init__(self, filename: str) -> None:
393
+ """Simple initialization of a `FileReporter`.
394
+
395
+ The `filename` argument is the path to the file being reported. This
396
+ will be available as the `.filename` attribute on the object. Other
397
+ method implementations on this base class rely on this attribute.
398
+
399
+ """
400
+ self.filename = filename
401
+
402
+ def __repr__(self) -> str:
403
+ return f"<{self.__class__.__name__} filename={self.filename!r}>"
404
+
405
+ def relative_filename(self) -> str:
406
+ """Get the relative file name for this file.
407
+
408
+ This file path will be displayed in reports. The default
409
+ implementation will supply the actual project-relative file path. You
410
+ only need to supply this method if you have an unusual syntax for file
411
+ paths.
412
+
413
+ """
414
+ return files.relative_filename(self.filename)
415
+
416
+ def source(self) -> str:
417
+ """Get the source for the file.
418
+
419
+ Returns a Unicode string.
420
+
421
+ The base implementation simply reads the `self.filename` file and
422
+ decodes it as UTF-8. Override this method if your file isn't readable
423
+ as a text file, or if you need other encoding support.
424
+
425
+ """
426
+ with open(self.filename, encoding="utf-8") as f:
427
+ return f.read()
428
+
429
+ def lines(self) -> set[TLineNo]:
430
+ """Get the executable lines in this file.
431
+
432
+ Your plug-in must determine which lines in the file were possibly
433
+ executable. This method returns a set of those line numbers.
434
+
435
+ Returns a set of line numbers.
436
+
437
+ """
438
+ _needs_to_implement(self, "lines")
439
+
440
+ def excluded_lines(self) -> set[TLineNo]:
441
+ """Get the excluded executable lines in this file.
442
+
443
+ Your plug-in can use any method it likes to allow the user to exclude
444
+ executable lines from consideration.
445
+
446
+ Returns a set of line numbers.
447
+
448
+ The base implementation returns the empty set.
449
+
450
+ """
451
+ return set()
452
+
453
+ def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]:
454
+ """Translate recorded lines into reported lines.
455
+
456
+ Some file formats will want to report lines slightly differently than
457
+ they are recorded. For example, Python records the last line of a
458
+ multi-line statement, but reports are nicer if they mention the first
459
+ line.
460
+
461
+ Your plug-in can optionally define this method to perform these kinds
462
+ of adjustment.
463
+
464
+ `lines` is a sequence of integers, the recorded line numbers.
465
+
466
+ Returns a set of integers, the adjusted line numbers.
467
+
468
+ The base implementation returns the numbers unchanged.
469
+
470
+ """
471
+ return set(lines)
472
+
473
+ def arcs(self) -> set[TArc]:
474
+ """Get the executable arcs in this file.
475
+
476
+ To support branch coverage, your plug-in needs to be able to indicate
477
+ possible execution paths, as a set of line number pairs. Each pair is
478
+ a `(prev, next)` pair indicating that execution can transition from the
479
+ `prev` line number to the `next` line number.
480
+
481
+ Returns a set of pairs of line numbers. The default implementation
482
+ returns an empty set.
483
+
484
+ """
485
+ return set()
486
+
487
+ def no_branch_lines(self) -> set[TLineNo]:
488
+ """Get the lines excused from branch coverage in this file.
489
+
490
+ Your plug-in can use any method it likes to allow the user to exclude
491
+ lines from consideration of branch coverage.
492
+
493
+ Returns a set of line numbers.
494
+
495
+ The base implementation returns the empty set.
496
+
497
+ """
498
+ return set()
499
+
500
+ def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]:
501
+ """Translate recorded arcs into reported arcs.
502
+
503
+ Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of
504
+ line number pairs.
505
+
506
+ Returns a set of line number pairs.
507
+
508
+ The default implementation returns `arcs` unchanged.
509
+
510
+ """
511
+ return set(arcs)
512
+
513
+ def exit_counts(self) -> dict[TLineNo, int]:
514
+ """Get a count of exits from that each line.
515
+
516
+ To determine which lines are branches, coverage.py looks for lines that
517
+ have more than one exit. This function creates a dict mapping each
518
+ executable line number to a count of how many exits it has.
519
+
520
+ To be honest, this feels wrong, and should be refactored. Let me know
521
+ if you attempt to implement this method in your plug-in...
522
+
523
+ """
524
+ return {}
525
+
526
+ def missing_arc_description(
527
+ self,
528
+ start: TLineNo,
529
+ end: TLineNo,
530
+ executed_arcs: Iterable[TArc] | None = None, # pylint: disable=unused-argument
531
+ ) -> str:
532
+ """Provide an English sentence describing a missing arc.
533
+
534
+ The `start` and `end` arguments are the line numbers of the missing
535
+ arc. Negative numbers indicate entering or exiting code objects.
536
+
537
+ The `executed_arcs` argument is a set of line number pairs, the arcs
538
+ that were executed in this file.
539
+
540
+ By default, this simply returns the string "Line {start} didn't jump
541
+ to {end}".
542
+
543
+ """
544
+ return f"Line {start} didn't jump to line {end}"
545
+
546
+ def arc_description(
547
+ self,
548
+ start: TLineNo, # pylint: disable=unused-argument
549
+ end: TLineNo,
550
+ ) -> str:
551
+ """Provide an English description of an arc's effect."""
552
+ return f"jump to line {end}"
553
+
554
+ def source_token_lines(self) -> TSourceTokenLines:
555
+ """Generate a series of tokenized lines, one for each line in `source`.
556
+
557
+ These tokens are used for syntax-colored reports.
558
+
559
+ Each line is a list of pairs, each pair is a token::
560
+
561
+ [("key", "def"), ("ws", " "), ("nam", "hello"), ("op", "("), ... ]
562
+
563
+ Each pair has a token class, and the token text. The token classes
564
+ are:
565
+
566
+ * ``"com"``: a comment
567
+ * ``"key"``: a keyword
568
+ * ``"nam"``: a name, or identifier
569
+ * ``"num"``: a number
570
+ * ``"op"``: an operator
571
+ * ``"str"``: a string literal
572
+ * ``"ws"``: some white space
573
+ * ``"txt"``: some other kind of text
574
+
575
+ If you concatenate all the token texts, and then join them with
576
+ newlines, you should have your original source back.
577
+
578
+ The default implementation simply returns each line tagged as
579
+ ``"txt"``.
580
+
581
+ """
582
+ for line in self.source().splitlines():
583
+ yield [("txt", line)]
584
+
585
+ def code_regions(self) -> Iterable[CodeRegion]:
586
+ """Identify regions in the source file for finer reporting than by file.
587
+
588
+ Returns an iterable of :class:`CodeRegion` objects. The kinds reported
589
+ should be in the possibilities returned by :meth:`code_region_kinds`.
590
+
591
+ """
592
+ return []
593
+
594
+ def code_region_kinds(self) -> Iterable[tuple[str, str]]:
595
+ """Return the kinds of code regions this plugin can find.
596
+
597
+ The returned pairs are the singular and plural forms of the kinds::
598
+
599
+ [
600
+ ("function", "functions"),
601
+ ("class", "classes"),
602
+ ]
603
+
604
+ This will usually be hard-coded, but could also differ by the specific
605
+ source file involved.
606
+
607
+ """
608
+ return []
609
+
610
+ def __eq__(self, other: Any) -> bool:
611
+ return isinstance(other, FileReporter) and self.filename == other.filename
612
+
613
+ def __lt__(self, other: Any) -> bool:
614
+ return isinstance(other, FileReporter) and self.filename < other.filename
615
+
616
+ # This object doesn't need to be hashed.
617
+ __hash__ = None # type: ignore[assignment]