Repository: pypa/pip
File: src/pip/_internal/utils/misc.py (Python)

Code before:
def redact_auth_from_url(url: str) -> str:
    """Replace the password in a given url with ****."""
    return _transform_url(url, _redact_netloc)[0]


def redact_auth_from_requirement(req: Requirement) -> str:
    """Replace the password in a given requirement url with ****."""
    if not req.url:
        return str(req)
    return str(req).replace(req.url, redact_auth_from_url(req.url))


@dataclass(frozen=True)
class HiddenText:
    secret: str
    redacted: str

    def __repr__(self) -> str:
        return f"<HiddenText {str(self)!r}>"

    def __str__(self) -> str:
        return self.redacted

    # This is useful for testing.
    def __eq__(self, other: object) -> bool:
        if type(self) is not type(other) or not isinstance(other, HiddenText):
            return False
        # The string being used for redaction doesn't also have to match,
        # just the raw, original string.
        return self.secret == other.secret


def hide_value(value: str) -> HiddenText:
    return HiddenText(value, redacted="****")


def hide_url(url: str) -> HiddenText:
    redacted = redact_auth_from_url(url)
    return HiddenText(url, redacted=redacted)


def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None:
    """Protection of pip.exe from modification on Windows

    On Windows, any operation modifying pip should be run as:
        python -m pip ...
    """
    pip_names = [
        "pip",
        f"pip{sys.version_info.major}",
        f"pip{sys.version_info.major}.{sys.version_info.minor}",

Review comment (security):
This one is really about type narrowing, since mypy doesn't do that for `type(self) is not type(other)`. The reason for the change at all comes down to the fact that ``self.secret == other.secret`` isn't recognised as a bool when `other` is `Any`.

Code after:
def redact_auth_from_url(url: str) -> str:
    """Replace the password in a given url with ****."""
    return _transform_url(url, _redact_netloc)[0]


def redact_auth_from_requirement(req: Requirement) -> str:
    """Replace the password in a given requirement url with ****."""
    if not req.url:
        return str(req)
    return str(req).replace(req.url, redact_auth_from_url(req.url))


@dataclass(frozen=True)
class HiddenText:
    secret: str
    redacted: str

    def __repr__(self) -> str:
        return f"<HiddenText {str(self)!r}>"

    def __str__(self) -> str:
        return self.redacted

    def __eq__(self, other: object) -> bool:
        # Equality is particularly useful for testing.
        if type(self) is type(other):
            # The string being used for redaction doesn't also have to match,
            # just the raw, original string.
            return self.secret == cast(HiddenText, other).secret
        return NotImplemented

    # Disable hashing, since we have a custom __eq__ and don't need hash-ability
    # (yet). The only required property of hashing is that objects which compare
    # equal have the same hash value.
    __hash__ = None  # type: ignore[assignment]


def hide_value(value: str) -> HiddenText:
    return HiddenText(value, redacted="****")


def hide_url(url: str) -> HiddenText:
    redacted = redact_auth_from_url(url)
    return HiddenText(url, redacted=redacted)


def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None:
    """Protection of pip.exe from modification on Windows

    On Windows, any operation modifying pip should be run as:
        python -m pip ...