GildedRose-Refactoring-Kata/.venv/lib/python3.12/site-packages/approvaltests/approvals.py
2025-06-22 13:36:01 +05:30

432 lines
16 KiB
Python

import argparse
import xml.dom.minidom
from contextlib import contextmanager
from pathlib import Path
from typing import Any, ByteString, Callable, Iterator, List, Optional
import approvaltests.namer.default_namer_factory
import approvaltests.reporters.default_reporter_factory
from approval_utilities import utils
from approval_utilities.approvaltests.core.executable_command import ExecutableCommand
from approval_utilities.approvaltests.core.verifiable import Verifiable
from approval_utilities.list_utils import format_list
from approval_utilities.utilities.exceptions.exception_utils import to_string
from approval_utilities.utilities.map_reduce import first
from approvaltests.approval_exception import ApprovalException
from approvaltests.binary_writer import BinaryWriter
from approvaltests.core import Reporter, Writer
from approvaltests.core.format_wrapper import AlwaysMatch, FormatWrapper
from approvaltests.core.namer import Namer
from approvaltests.core.options import Options
from approvaltests.core.scenario_namer import ScenarioNamer
from approvaltests.existing_file_writer import ExistingFileWriter
from approvaltests.file_approver import FileApprover
from approvaltests.namer.namer_base import NamerBase
from approvaltests.namer.stack_frame_namer import StackFrameNamer
from approvaltests.reporters.diff_reporter import DiffReporter
from approvaltests.reporters.executable_command_reporter import (
ExecutableCommandReporter,
)
from approvaltests.string_writer import StringWriter
from approvaltests.verifiable_objects.formatter_of_argparse_namespace import (
FormatterWrapperOfArgparseNamespace,
)
__unittest = True
__tracebackhide__ = True
class Settings:
def allow_multiple_verify_calls_for_this_method(self) -> None:
class_and_method = get_default_namer().get_file_name()
def allow_method(filename: str) -> bool:
return class_and_method in filename
# make it so we do not raise an eerror if someonee calls it twice
FileApprover.add_allowed_duplicates(allow_method)
def settings() -> Settings:
return Settings()
def set_default_reporter(reporter: Reporter) -> None:
return approvaltests.reporters.default_reporter_factory.set_default_reporter(
reporter
)
def get_default_reporter() -> Reporter:
return approvaltests.reporters.default_reporter_factory.get_default_reporter()
def get_reporter(reporter: Optional[Reporter]) -> Reporter:
return approvaltests.reporters.default_reporter_factory.get_reporter(reporter)
def get_default_namer(extension: Optional[str] = None) -> NamerBase:
from approvaltests.namer.default_name import get_default_namer as get_namer
return get_namer(extension)
def verify(
data: Any,
reporter: Optional[Reporter] = None,
namer: Optional[Namer] = None,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
"""Verify string data against a previously approved version of the string.
Args:
data: A string containing the data to be compared with approved data from a previous run.
On Python 2 this can be a bytes, str, or unicode object. On Python 3 this should be
a str object.
reporter: An optional Reporter. If None (the default), the default reporter
will be used; see get_default_reporter().
encoding: An optional encoding to be used when serialising the data to a byte stream for
comparison. If None (the default) a locale-dependent encoding will be used; see
locale.getpreferredencoding().
errors: An optional string that specifies how encoding and decoding errors are to be handled
If None (the default) or 'strict', raise a ValueError exception if there is an encoding
error. Pass 'ignore' to ignore encoding errors. Pass 'replace' to use a replacement
marker (such as '?') when there is malformed data.
newline: An optional string that controls how universal newlines work when comparing data.
It can be None, '', '\n', '\r', and '\r\n'. If None (the default) universal newlines are
enabled and any '\n' characters are translated to the system default line separator
given by os.linesep. If newline is '', no translation takes place. If newline is any of
the other legal values, any '\n' characters written are translated to the given string.
Raises:
ApprovalException: If the verification fails because the given string does not match the
approved string.
ValueError: If data cannot be encoded using the specified encoding when errors is set to
None or 'strict'.
"""
data = find_formatter_for_specified_class(data)
options = initialize_options(options, reporter)
if isinstance(data, Verifiable):
parameters = data.get_verify_parameters(options)
options = parameters.options
namer_to_use = namer or options.namer
verify_with_namer(
data,
namer_to_use,
encoding=encoding,
errors=errors,
newline=newline,
options=options,
)
format_wrappers = [FormatterWrapperOfArgparseNamespace(), AlwaysMatch()]
@contextmanager
def register_formatter(formatter: FormatWrapper) -> Iterator[None]:
format_wrappers.insert(0, formatter)
yield
format_wrappers.remove(formatter)
def find_formatter_for_specified_class(data: Any) -> Any:
wrapper = first(format_wrappers, lambda f: f.is_match(data))
return wrapper.wrap(data)
def verify_binary(
data: ByteString,
file_extension_with_dot: str,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
options = initialize_options(options, None).for_file.with_extension(
file_extension_with_dot
)
verify_with_namer_and_writer(
options.namer,
BinaryWriter(data, file_extension_with_dot),
None,
options=options,
)
def initialize_options(
options: Optional[Options], reporter: Optional[Reporter] = None
) -> Options:
if options is None:
options = Options()
if reporter is not None:
options = options.with_reporter(reporter)
return options
def verify_with_namer(
data: Any,
namer: Namer,
reporter: Optional[Reporter] = None,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
"""Verify string data against a previously approved version of the string.
Args:
data: A string containing the data to be compared with approved data from a previous run.
On Python 2 this can be a bytes, str, or unicode object. On Python 3 this should be
a str object.
namer: A Namer instance used for naming approved and received data files.
reporter: An optional Reporter. If None (the default), the default reporter
will be used; see get_default_reporter().
encoding: An optional encoding to be used when serialising the data to a byte stream for
comparison. If None (the default) a locale-dependent encoding will be used; see
locale.getpreferredencoding().
errors: An optional string that specifies how encoding and decoding errors are to be handled
If None (the default) or 'strict', raise a ValueError exception if there is an encoding
error. Pass 'ignore' to ignore encoding errors. Pass 'replace' to use a replacement
marker (such as '?') when there is malformed data.
newline: An optional string that controls how universal newlines work when comparing data.
It can be None, '', '\n', '\r', and '\r\n'. If None (the default) universal newlines are
enabled and any '\n' characters are translated to the system default line separator
given by os.linesep. If newline is '', no translation takes place. If newline is any of
the other legal values, any '\n' characters written are translated to the given string.
Raises:
ApprovalException: If the verification fails because the given string does not match the
approved string.
ValueError: If data cannot be encoded using the specified encoding when errors is set to
None or 'strict'.
"""
options = initialize_options(options, reporter)
writer = StringWriter(
options.scrub(str(data)), encoding=encoding, errors=errors, newline=newline
)
verify_with_namer_and_writer(namer, writer, options=options)
def verify_with_namer_and_writer(
namer: Namer,
writer: Writer,
reporter: Optional[Reporter] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
options = initialize_options(options, reporter)
error = FileApprover.verify(namer, writer, options.reporter, options.comparator)
if error:
raise ApprovalException(error)
# begin-snippet: verify_as_json
def verify_as_json(
object_to_verify: Any,
reporter: Optional[Reporter] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
deserialize_json_fields: bool = False,
options: Optional[Options] = None,
) -> None:
if deserialize_json_fields:
object_to_verify = utils.deserialize_json_fields(object_to_verify)
options = initialize_options(options, reporter)
json_text = utils.to_json(object_to_verify) + "\n"
verify(
json_text,
None,
encoding="utf-8",
newline="\n",
options=options.for_file.with_extension(".json"),
)
# end-snippet
def verify_xml(
xml_string: str,
reporter: Optional[Reporter] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
options = initialize_options(options, reporter)
try:
dom = xml.dom.minidom.parseString(xml_string)
pretty_xml = dom.toprettyxml()
except Exception:
pretty_xml = xml_string
verify(pretty_xml, options=options.for_file.with_extension(".xml"))
def verify_html(
html_string: str,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
options = initialize_options(options)
try:
from bs4 import BeautifulSoup
pretty_html = BeautifulSoup(html_string, "html.parser").prettify()
except Exception:
pretty_html = html_string
verify(pretty_html, options=options.for_file.with_extension(".html"))
def verify_file(
file_name: str,
reporter: Optional[Reporter] = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
"""Verify the contents of a text file against previously approved contents.
Args:
file_name: The path to a file. The file will be opened in text mode.
reporter: An optional Reporter. If None (the default), the default reporter
will be used; see get_default_reporter().
Raises:
ApprovalException: If the verification fails because the given string does not match the
approved string.
ValueError: If data cannot be encoded using the specified encoding when errors is set to
None or 'strict'.
"""
options = initialize_options(options, reporter)
options = options.for_file.with_extension(Path(file_name).suffix, no_override=True)
verify_with_namer_and_writer(
options.namer, ExistingFileWriter(file_name, options), None, options=options
)
def verify_all(
header: str,
alist: List[Any],
formatter: Optional[Callable] = None,
reporter: Optional[DiffReporter] = None,
encoding: None = None,
errors: None = None,
newline: None = None,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
"""Verify a collection of items against a previously approved collection.
Args:
header: A header line string to be included before the list of items.
alist: An iterable series of objects, a string representation of each of which will be
included in an aggregated string for comparison.
formatter: An optional object which must have a print_item(x) method such that
formatter.print_item(x) will return a string representation of a single item from alist.
reporter: An optional Reporter. If None (the default), the default reporter
will be used; see get_default_reporter().
encoding: An optional encoding to be used when serialising the data to a byte stream for
comparison. If None (the default) a locale-dependent encoding will be used; see
locale.getpreferredencoding().
errors: An optional string that specifies how encoding and decoding errors are to be handled
If None (the default) or 'strict', raise a ValueError exception if there is an encoding
error. Pass 'ignore' to ignore encoding errors. Pass 'replace' to use a replacement
marker (such as '?') when there is malformed data.
newline: An optional string that controls how universal newlines work when comparing data.
It can be None, '', '\n', '\r', and '\r\n'. If None (the default) universal newlines are
enabled and any '\n' characters are translated to the system default line separator
given by os.linesep. If newline is '', no translation takes place. If newline is any of
the other legal values, any '\n' characters written are translated to the given string.
Raises:
ApprovalException: If the verification fails because the given string does not match the
approved string.
ValueError: If data cannot be encoded using the specified encoding when errors is set to
None or 'strict'.
"""
options = initialize_options(options, reporter)
text = format_list(alist, formatter, header)
verify(
text, None, encoding=encoding, errors=errors, newline=newline, options=options
)
def verify_exception(
code_that_throws_exception: Callable,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
result = ""
try:
code_that_throws_exception()
result = "No exception was thrown"
except BaseException as exception:
result = to_string(exception)
verify(result, options=options)
def get_scenario_namer(*scenario_name: Any) -> ScenarioNamer:
return ScenarioNamer(get_default_namer(), *scenario_name)
def delete_approved_file() -> None:
filename = Path(get_default_namer().get_approved_filename())
if filename.exists():
filename.unlink()
def verify_executable_command(
command: ExecutableCommand,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
options = initialize_options(options)
verify(
command.get_command(),
options=options.with_reporter(
ExecutableCommandReporter(command, options.reporter)
),
)
def verify_argument_parser(
parser: argparse.ArgumentParser,
*, # enforce keyword arguments - https://www.python.org/dev/peps/pep-3102/
options: Optional[Options] = None,
) -> None:
parser.formatter_class = lambda prog: argparse.HelpFormatter(prog, width=200)
options = options or Options()
scrubber = lambda t: t.replace("options:", "<optional header>:").replace(
"optional arguments:", "<optional header>:"
)
verify(
parser.format_help(),
options=options.with_scrubber(scrubber),
)