Repository URL to install this package:
|
Version:
1.26.0.dev0+gite506aa5f ▾
|
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
from collections import defaultdict
from dataclasses import dataclass
from typing import DefaultDict, Iterator, List, Optional
from pants.option.ranked_value import RankedValue
# TODO: Get rid of this? The parser should be able to lazily track.
class OptionTracker:
"""Records a history of what options are set and where they came from."""
@dataclass(frozen=True)
class OptionHistoryRecord:
value: str
rank: int
deprecation_version: Optional[str]
details: Optional[str]
class OptionHistory:
"""Tracks the history of an individual option."""
def __init__(self) -> None:
self.values: List["OptionTracker.OptionHistoryRecord"] = []
def record_value(
self,
value: str,
rank: int,
deprecation_version: Optional[str],
details: Optional[str] = None,
) -> None:
"""Record that the option was set to the given value at the given rank.
:param value: the value the option was set to.
:param rank: the rank of the option when it was set to this value.
:param deprecation_version: Deprecation version for this option.
:param details: optional elaboration of where the option came from (eg, a particular
config file).
"""
deprecation_version_to_write = deprecation_version
if self.latest is not None:
if self.latest.rank > rank:
return
if self.latest.value == value:
return # No change.
if self.latest.deprecation_version:
# Higher RankedValue may not contain deprecation version, so this make sure
# deprecation_version propagate to later and higher ranked value since it is immutable
deprecation_version_to_write = (
self.latest.deprecation_version or deprecation_version
)
self.values.append(
OptionTracker.OptionHistoryRecord(
value, rank, deprecation_version_to_write, details
)
)
@property
def was_overridden(self) -> bool:
"""A value was overridden if it has rank greater than 'HARDCODED'."""
if self.latest is None or len(self.values) < 2:
return False
return (
self.latest.rank > RankedValue.HARDCODED and self.values[-2].rank > RankedValue.NONE
)
@property
def latest(self) -> Optional["OptionTracker.OptionHistoryRecord"]:
"""The most recent value this option was set to, or None if it was never set."""
return self.values[-1] if self.values else None
def __iter__(self) -> Iterator["OptionTracker.OptionHistoryRecord"]:
for record in self.values:
yield record
def __len__(self) -> int:
return len(self.values)
def __init__(self) -> None:
self.option_history_by_scope: DefaultDict = defaultdict(dict)
def record_option(
self,
scope: str,
option: str,
value: str,
rank: int,
deprecation_version: Optional[str] = None,
details: Optional[str] = None,
) -> None:
"""Records that the given option was set to the given value.
:param scope: scope of the option.
:param option: name of the option.
:param value: value the option was set to.
:param rank: the rank of the option (Eg, RankedValue.HARDCODED), to keep track of where the
option came from.
:param deprecation_version: Deprecation version for this option.
:param details: optional additional details about how the option was set (eg, the name
of a particular config file, if the rank is RankedValue.CONFIG).
"""
scoped_options = self.option_history_by_scope[scope]
if option not in scoped_options:
scoped_options[option] = self.OptionHistory()
scoped_options[option].record_value(value, rank, deprecation_version, details)