Skip to content

Commit

Permalink
future __annotations__ and pyproject-fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
RonnyPfannschmidt committed Nov 30, 2023
1 parent f5af2d7 commit a258609
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 41 deletions.
11 changes: 8 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:
- exclude: "(?x)^(\n environments/.*/secret.*|\n .*\\.patch\n)$\n"
id: check-toml
repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
- hooks:
- args:
- --profile
Expand All @@ -29,8 +29,13 @@ repos:
- hooks:
- id: black
repo: https://github.com/psf/black
rev: 23.3.0
rev: 23.11.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.289
rev: v0.1.6
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "1.5.2"
hooks:
- id: pyproject-fmt
35 changes: 21 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
requires = [
"hatchling",
]

[project]
name = "pytest-patterns"
dynamic = ["version"]
description = 'pytest plugin to make testing complicated long string output easy to write and easy to debug'
description = "pytest plugin to make testing complicated long string output easy to write and easy to debug"
readme = "README.md"
requires-python = ">=3.7"
keywords = [
]
license = "MIT"
keywords = []
authors = [
{ name = "Christian Theune", email = "[email protected]" },
]
requires-python = ">=3.7"
classifiers = [
"Framework :: Pytest",
"Development Status :: 4 - Beta",
"Framework :: Pytest",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = ["pytest>=7",]

dynamic = [
"version",
]
dependencies = [
"pytest>=7",
]
[project.urls]
Documentation = "https://github.com/unknown/pytest-patterns#readme"
Issues = "https://github.com/unknown/pytest-patterns/issues"
Source = "https://github.com/unknown/pytest-patterns"

[project.entry-points.pytest11]
myproject = "pytest_patterns.plugin"

Expand Down Expand Up @@ -141,6 +148,10 @@ ban-relative-imports = "all"
# Tests can use magic values, assertions, and relative imports
"tests/**/*" = ["PLR2004", "S101", "TID252"]

[tool.isort]
profile = "black"
line_length = 80

[tool.coverage.run]
source_pkgs = ["pytest_patterns", "tests"]
branch = true
Expand All @@ -160,10 +171,6 @@ exclude_lines = [
"if TYPE_CHECKING:",
]

[tool.isort]
profile = "black"
line_length = 80

[tool.mypy]
strict=true
python_version = "3.8"
python_version = "3.8"
39 changes: 21 additions & 18 deletions src/pytest_patterns/plugin.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
from __future__ import annotations

import enum
import re
from typing import List, Set, Tuple, Any, Iterator, Optional, Union
from typing import Any, Iterator

import pytest


@pytest.fixture
def patterns() -> "PatternsLib":
def patterns() -> PatternsLib:
return PatternsLib()


def pytest_assertrepr_compare(op: str, left: Any, right: Any) -> Optional[List[str]]:
def pytest_assertrepr_compare(
op: str, left: Any, right: Any
) -> list[str] | None:
if op != "==":
return None
if isinstance(left, Pattern):
Expand All @@ -21,7 +25,6 @@ def pytest_assertrepr_compare(op: str, left: Any, right: Any) -> Optional[List[s
return None



class Status(enum.Enum):
UNEXPECTED = 1
OPTIONAL = 2
Expand All @@ -43,7 +46,7 @@ def symbol(self) -> str:
EMPTY_LINE_PATTERN = "<empty-line>"


def match(pattern: str, line: str) -> Optional[ Union[bool, "re.Match[str]"]]:
def match(pattern: str, line: str) -> bool | re.Match[str] | None:
if pattern == EMPTY_LINE_PATTERN:
if not line:
return True
Expand Down Expand Up @@ -74,9 +77,9 @@ def mark(self, status: Status, cause: str) -> None:


class Audit:
content: List[Line]
unmatched_expectations: List[Tuple[str, str]]
matched_refused: Set[Tuple[str, str]]
content: list[Line]
unmatched_expectations: list[tuple[str, str]]
matched_refused: set[tuple[str, str]]

def __init__(self, content: str):
self.unmatched_expectations = []
Expand All @@ -89,7 +92,7 @@ def __init__(self, content: str):
def cursor(self) -> Iterator[Line]:
return iter(self.content)

def in_order(self, name: str, expected_lines: List[str]) -> None:
def in_order(self, name: str, expected_lines: list[str]) -> None:
"""Expect all lines exist and come in order, but they
may be interleaved with other lines."""
cursor = self.cursor()
Expand All @@ -103,7 +106,7 @@ def in_order(self, name: str, expected_lines: List[str]) -> None:
# Reset the scan, maybe the other lines will match
cursor = self.cursor()

def optional(self, name: str, tolerated_lines: List[str]) -> None:
def optional(self, name: str, tolerated_lines: list[str]) -> None:
"""Those lines may exist and then they may appear anywhere
a number of times, or they may not exist.
"""
Expand All @@ -112,14 +115,14 @@ def optional(self, name: str, tolerated_lines: List[str]) -> None:
if line.matches(tolerated_line):
line.mark(Status.OPTIONAL, name)

def refused(self, name: str, refused_lines: List[str]) -> None:
def refused(self, name: str, refused_lines: list[str]) -> None:
for refused_line in refused_lines:
for line in self.cursor():
if line.matches(refused_line):
line.mark(Status.REFUSED, name)
self.matched_refused.add((name, refused_line))

def continuous(self, name: str, continuous_lines: List[str]) -> None:
def continuous(self, name: str, continuous_lines: list[str]) -> None:
continuous_cursor = enumerate(continuous_lines)
continuous_index, continuous_line = next(continuous_cursor)
for line in self.cursor():
Expand Down Expand Up @@ -195,18 +198,18 @@ def format_line_report(symbol: str, cause: str, line: str) -> str:
return symbol + " " + cause.ljust(15)[:15] + " | " + line


def pattern_lines(lines: str) -> List[str]:
def pattern_lines(lines: str) -> list[str]:
# Remove leading whitespace, ignore empty lines.
return list(filter(None, lines.splitlines()))


class Pattern:
name: str
library: "PatternsLib"
ops: List[Tuple[str, str, Any]]
inherited: Set[str]
library: PatternsLib
ops: list[tuple[str, str, Any]]
inherited: set[str]

def __init__(self, library: "PatternsLib", name: str):
def __init__(self, library: PatternsLib, name: str):
self.name = name
self.library = library
self.ops = []
Expand Down Expand Up @@ -241,7 +244,7 @@ def refused(self, lines: str) -> None:

# Internal API

def flat_ops(self) -> Iterator[Tuple[str, str, Any]]:
def flat_ops(self) -> Iterator[tuple[str, str, Any]]:
for inherited_pattern in self.inherited:
yield from getattr(self.library, inherited_pattern).flat_ops()
yield from self.ops
Expand Down
19 changes: 13 additions & 6 deletions tests/test_basics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest

from pytest_patterns.plugin import PatternsLib

GENERIC_HEADER = [
Expand All @@ -15,7 +16,7 @@ def test_patternslib_multiple_accesses(patterns: PatternsLib) -> None:
assert patterns.foo is patterns.foo


def test_empty_pattern_empty_string_is_ok(patterns:PatternsLib) -> None:
def test_empty_pattern_empty_string_is_ok(patterns: PatternsLib) -> None:
# This is fine IMHO. The whole general assumption is that we only reject
# unexpected content and fail if required content is missing. If there is
# no content, then there is no unexpected content and if you didn't expect
Expand All @@ -25,7 +26,7 @@ def test_empty_pattern_empty_string_is_ok(patterns:PatternsLib) -> None:
assert audit.is_ok()


def test_unexpected_lines_fail(patterns:PatternsLib) -> None:
def test_unexpected_lines_fail(patterns: PatternsLib) -> None:
audit = patterns.nothing._audit("This is an unexpected line")
assert list(audit.report()) == [
*GENERIC_HEADER,
Expand All @@ -34,7 +35,7 @@ def test_unexpected_lines_fail(patterns:PatternsLib) -> None:
assert not audit.is_ok()


def test_empty_lines_do_not_match(patterns :PatternsLib) -> None:
def test_empty_lines_do_not_match(patterns: PatternsLib) -> None:
patterns.nothing.optional("")
audit = patterns.nothing._audit(
"""
Expand Down Expand Up @@ -107,7 +108,9 @@ def test_comprehensive(patterns: PatternsLib) -> None:
)


def test_in_order_lines_clear_with_intermittent_input(patterns: PatternsLib) -> None:
def test_in_order_lines_clear_with_intermittent_input(
patterns: PatternsLib,
) -> None:
pattern = patterns.in_order
pattern.in_order(
"""
Expand Down Expand Up @@ -173,7 +176,9 @@ def test_refused_lines_fail(patterns: PatternsLib) -> None:
assert not audit.is_ok()


def test_continuous_lines_only_clear_if_not_interrupted(patterns: PatternsLib) -> None:
def test_continuous_lines_only_clear_if_not_interrupted(
patterns: PatternsLib,
) -> None:
pattern = patterns.focus
pattern.optional("asdf")
pattern.continuous(
Expand Down Expand Up @@ -240,7 +245,9 @@ def test_continuous_lines_only_clear_if_not_interrupted(patterns: PatternsLib) -
assert not audit.is_ok()


def test_continuous_lines_fail_and_report_if_first_line_isnt_matching(patterns: PatternsLib) -> None:
def test_continuous_lines_fail_and_report_if_first_line_isnt_matching(
patterns: PatternsLib,
) -> None:
pattern = patterns.focus
pattern.continuous(
"""
Expand Down

0 comments on commit a258609

Please sign in to comment.