Flexible matching of text. This is a convenience class to test large texts like the output of commands. The constructor takes a list of lines without their trailing newlines, i.e. ``text.splitlines()``.
| 1570 | |
| 1571 | |
| 1572 | class LineMatcher: |
| 1573 | """Flexible matching of text. |
| 1574 | |
| 1575 | This is a convenience class to test large texts like the output of |
| 1576 | commands. |
| 1577 | |
| 1578 | The constructor takes a list of lines without their trailing newlines, i.e. |
| 1579 | ``text.splitlines()``. |
| 1580 | """ |
| 1581 | |
| 1582 | def __init__(self, lines: list[str]) -> None: |
| 1583 | self.lines = lines |
| 1584 | self._log_output: list[str] = [] |
| 1585 | |
| 1586 | def __str__(self) -> str: |
| 1587 | """Return the entire original text. |
| 1588 | |
| 1589 | .. versionadded:: 6.2 |
| 1590 | You can use :meth:`str` in older versions. |
| 1591 | """ |
| 1592 | return "\n".join(self.lines) |
| 1593 | |
| 1594 | def _getlines(self, lines2: str | Sequence[str] | Source) -> Sequence[str]: |
| 1595 | if isinstance(lines2, str): |
| 1596 | lines2 = Source(lines2) |
| 1597 | if isinstance(lines2, Source): |
| 1598 | lines2 = lines2.strip().lines |
| 1599 | return lines2 |
| 1600 | |
| 1601 | def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: |
| 1602 | """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" |
| 1603 | __tracebackhide__ = True |
| 1604 | self._match_lines_random(lines2, fnmatch) |
| 1605 | |
| 1606 | def re_match_lines_random(self, lines2: Sequence[str]) -> None: |
| 1607 | """Check lines exist in the output in any order (using :func:`python:re.match`).""" |
| 1608 | __tracebackhide__ = True |
| 1609 | self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) |
| 1610 | |
| 1611 | def _match_lines_random( |
| 1612 | self, lines2: Sequence[str], match_func: Callable[[str, str], bool] |
| 1613 | ) -> None: |
| 1614 | __tracebackhide__ = True |
| 1615 | lines2 = self._getlines(lines2) |
| 1616 | for line in lines2: |
| 1617 | for x in self.lines: |
| 1618 | if line == x or match_func(x, line): |
| 1619 | self._log("matched: ", repr(line)) |
| 1620 | break |
| 1621 | else: |
| 1622 | msg = f"line {line!r} not found in output" |
| 1623 | self._log(msg) |
| 1624 | self._fail(msg) |
| 1625 | |
| 1626 | def get_lines_after(self, fnline: str) -> Sequence[str]: |
| 1627 | """Return all lines following the given line in the text. |
| 1628 | |
| 1629 | The given line can contain glob wildcards. |
no outgoing calls