Skip to content

Commit 026d924

Browse files
committedFeb 14, 2023
refactor: no placebos, use true Optional
For objects that truly might not exist, use Optional. Some objects will always exist eventually, and for those we have some null implementation standins to use without making new placebo classes.
1 parent d06ace2 commit 026d924

File tree

3 files changed

+40
-50
lines changed

3 files changed

+40
-50
lines changed
 

‎coverage/control.py

+39-48
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,10 @@ def __init__( # pylint: disable=too-many-arguments
249249
# Other instance attributes, set with placebos or placeholders.
250250
# More useful objects will be created later.
251251
self._debug: DebugControl = NoDebugging()
252-
self._inorout: InOrOut = _InOrOutPlacebo()
252+
self._inorout: Optional[InOrOut] = None
253253
self._plugins: Plugins = Plugins()
254-
self._data: CoverageData = _CoverageDataPlacebo()
255-
self._collector: Collector = _CollectorPlacebo()
254+
self._data: Optional[CoverageData] = None
255+
self._collector: Optional[Collector] = None
256256

257257
self._file_mapper: Callable[[str], str] = abs_file
258258
self._data_suffix = self._run_suffix = None
@@ -378,6 +378,7 @@ def _should_trace(self, filename: str, frame: FrameType) -> TFileDisposition:
378378
Calls `_should_trace_internal`, and returns the FileDisposition.
379379
380380
"""
381+
assert self._inorout is not None
381382
disp = self._inorout.should_trace(filename, frame)
382383
if self._debug.should('trace'):
383384
self._debug.write(disposition_debug_msg(disp))
@@ -389,6 +390,7 @@ def _check_include_omit_etc(self, filename: str, frame: FrameType) -> bool:
389390
Returns a boolean: True if the file should be traced, False if not.
390391
391392
"""
393+
assert self._inorout is not None
392394
reason = self._inorout.check_include_omit_etc(filename, frame)
393395
if self._debug.should('trace'):
394396
if not reason:
@@ -484,12 +486,14 @@ def set_option(self, option_name: str, value: Union[TConfigValueIn, TConfigSecti
484486
def load(self) -> None:
485487
"""Load previously-collected coverage data from the data file."""
486488
self._init()
487-
self._collector.reset()
489+
if self._collector is not None:
490+
self._collector.reset()
488491
should_skip = self.config.parallel and not os.path.exists(self.config.data_file)
489492
if not should_skip:
490493
self._init_data(suffix=None)
491494
self._post_init()
492495
if not should_skip:
496+
assert self._data is not None
493497
self._data.read()
494498

495499
def _init_for_start(self) -> None:
@@ -541,6 +545,7 @@ def _init_for_start(self) -> None:
541545

542546
self._init_data(suffix)
543547

548+
assert self._data is not None
544549
self._collector.use_data(self._data, self.config.context)
545550

546551
# Early warning if we aren't going to be able to support plugins.
@@ -584,7 +589,7 @@ def _init_for_start(self) -> None:
584589

585590
def _init_data(self, suffix: Optional[Union[str, bool]]) -> None:
586591
"""Create a data file if we don't have one yet."""
587-
if not self._data._real:
592+
if self._data is None:
588593
# Create the data file. We do this at construction time so that the
589594
# data file will be written into the directory where the process
590595
# started rather than wherever the process eventually chdir'd to.
@@ -614,6 +619,9 @@ def start(self) -> None:
614619
self._init_for_start()
615620
self._post_init()
616621

622+
assert self._collector is not None
623+
assert self._inorout is not None
624+
617625
# Issue warnings for possible problems.
618626
self._inorout.warn_conflicting_settings()
619627

@@ -635,6 +643,7 @@ def stop(self) -> None:
635643
if self._instances[-1] is self:
636644
self._instances.pop()
637645
if self._started:
646+
assert self._collector is not None
638647
self._collector.stop()
639648
self._started = False
640649

@@ -664,10 +673,12 @@ def erase(self) -> None:
664673
"""
665674
self._init()
666675
self._post_init()
667-
self._collector.reset()
676+
if self._collector is not None:
677+
self._collector.reset()
668678
self._init_data(suffix=None)
679+
assert self._data is not None
669680
self._data.erase(parallel=self.config.parallel)
670-
self._data = _CoverageDataPlacebo()
681+
self._data = None
671682
self._inited_for_start = False
672683

673684
def switch_context(self, new_context: str) -> None:
@@ -686,6 +697,7 @@ def switch_context(self, new_context: str) -> None:
686697
if not self._started: # pragma: part started
687698
raise CoverageException("Cannot switch context, coverage is not started")
688699

700+
assert self._collector is not None
689701
if self._collector.should_start_context:
690702
self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True)
691703

@@ -791,6 +803,7 @@ def combine(
791803
self._post_init()
792804
self.get_data()
793805

806+
assert self._data is not None
794807
combine_parallel_data(
795808
self._data,
796809
aliases=self._make_aliases(),
@@ -814,13 +827,15 @@ def get_data(self) -> CoverageData:
814827
self._init_data(suffix=None)
815828
self._post_init()
816829

817-
for plugin in self._plugins:
818-
if not plugin._coverage_enabled:
819-
self._collector.plugin_was_disabled(plugin)
830+
if self._collector is not None:
831+
for plugin in self._plugins:
832+
if not plugin._coverage_enabled:
833+
self._collector.plugin_was_disabled(plugin)
820834

821-
if self._collector.flush_data():
822-
self._post_save_work()
835+
if self._collector.flush_data():
836+
self._post_save_work()
823837

838+
assert self._data is not None
824839
return self._data
825840

826841
def _post_save_work(self) -> None:
@@ -830,6 +845,9 @@ def _post_save_work(self) -> None:
830845
Look for un-executed files.
831846
832847
"""
848+
assert self._data is not None
849+
assert self._inorout is not None
850+
833851
# If there are still entries in the source_pkgs_unmatched list,
834852
# then we never encountered those packages.
835853
if self._warn_unimported_source:
@@ -903,6 +921,7 @@ def _analyze(self, it: Union[FileReporter, TMorf]) -> Analysis:
903921

904922
def _get_file_reporter(self, morf: TMorf) -> FileReporter:
905923
"""Get a FileReporter for a module or file name."""
924+
assert self._data is not None
906925
plugin = None
907926
file_reporter: Union[str, FileReporter] = "python"
908927

@@ -938,6 +957,7 @@ def _get_file_reporters(self, morfs: Optional[Iterable[TMorf]] = None) -> List[F
938957
measured is used to find the FileReporters.
939958
940959
"""
960+
assert self._data is not None
941961
if not morfs:
942962
morfs = self._data.measured_files()
943963

@@ -952,7 +972,8 @@ def _prepare_data_for_reporting(self) -> None:
952972
"""Re-map data before reporting, to get implicit 'combine' behavior."""
953973
if self.config.paths:
954974
mapped_data = CoverageData(warn=self._warn, debug=self._debug, no_disk=True)
955-
mapped_data.update(self._data, aliases=self._make_aliases())
975+
if self._data is not None:
976+
mapped_data.update(self._data, aliases=self._make_aliases())
956977
self._data = mapped_data
957978

958979
def report(
@@ -1256,7 +1277,7 @@ def plugin_info(plugins: List[Any]) -> List[str]:
12561277
info = [
12571278
('coverage_version', covmod.__version__),
12581279
('coverage_module', covmod.__file__),
1259-
('tracer', self._collector.tracer_name()),
1280+
('tracer', self._collector.tracer_name() if self._collector is not None else "-none-"),
12601281
('CTracer', 'available' if HAS_CTRACER else "unavailable"),
12611282
('plugins.file_tracers', plugin_info(self._plugins.file_tracers)),
12621283
('plugins.configurers', plugin_info(self._plugins.configurers)),
@@ -1267,7 +1288,7 @@ def plugin_info(plugins: List[Any]) -> List[str]:
12671288
('config_contents',
12681289
repr(self.config._config_contents) if self.config._config_contents else '-none-'
12691290
),
1270-
('data_file', self._data.data_filename()),
1291+
('data_file', self._data.data_filename() if self._data is not None else "-none-"),
12711292
('python', sys.version.replace('\n', '')),
12721293
('platform', platform.platform()),
12731294
('implementation', platform.python_implementation()),
@@ -1288,44 +1309,14 @@ def plugin_info(plugins: List[Any]) -> List[str]:
12881309
('command_line', " ".join(getattr(sys, 'argv', ['-none-']))),
12891310
]
12901311

1291-
info.extend(self._inorout.sys_info())
1312+
if self._inorout is not None:
1313+
info.extend(self._inorout.sys_info())
1314+
12921315
info.extend(CoverageData.sys_info())
12931316

12941317
return info
12951318

12961319

1297-
class _Placebo:
1298-
"""Base class for placebos, to prevent calling the real base class __init__."""
1299-
def __init__(self) -> None:
1300-
...
1301-
1302-
1303-
class _CoverageDataPlacebo(_Placebo, CoverageData):
1304-
"""Just enough of a CoverageData to be used when we don't have a real one."""
1305-
_real = False
1306-
1307-
def data_filename(self) -> str:
1308-
return "-none-"
1309-
1310-
1311-
class _CollectorPlacebo(_Placebo, Collector):
1312-
"""Just enough of a Collector to be used when we don't have a real one."""
1313-
def reset(self) -> None:
1314-
...
1315-
1316-
def flush_data(self) -> bool:
1317-
return False
1318-
1319-
def tracer_name(self) -> str:
1320-
return "-none-"
1321-
1322-
1323-
class _InOrOutPlacebo(_Placebo, InOrOut):
1324-
"""Just enough of an InOrOut to be used when we don't have a real one."""
1325-
def sys_info(self) -> Iterable[Tuple[str, Any]]:
1326-
return []
1327-
1328-
13291320
# Mega debugging...
13301321
# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage.
13311322
if int(os.environ.get("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging

‎coverage/sqldata.py

-2
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ class CoverageData(AutoReprMixin):
212212
213213
"""
214214

215-
_real = True # to distinguish from a placebo in control.py
216-
217215
def __init__(
218216
self,
219217
basename: Optional[FilePath] = None,

‎tests/test_oddball.py

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def recur(n):
124124
with swallow_warnings("Trace function changed, data is likely wrong: None"):
125125
self.start_import_stop(cov, "recur")
126126

127+
assert cov._collector is not None
127128
pytrace = (cov._collector.tracer_name() == "PyTracer")
128129
expected_missing = [3]
129130
if pytrace: # pragma: no metacov

0 commit comments

Comments
 (0)
Please sign in to comment.