Skip to content

Commit

Permalink
Merge pull request #583 from twisted/falsified
Browse files Browse the repository at this point in the history
remove hypothesis (at least for now)
  • Loading branch information
glyph authored May 2, 2023
2 parents 1e3277c + c555bfb commit 9d91f10
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 144 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
*~
.DS_Store
/.eggs
/.hypothesis/
/.tox/
/apidocs/
/build/
Expand Down
5 changes: 0 additions & 5 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ ignore_missing_imports = True
[mypy-treq.*]
ignore_missing_imports = True

[mypy-hypothesis]
ignore_missing_imports = True
[mypy-hypothesis.*]
ignore_missing_imports = True

[mypy-idna]
ignore_missing_imports = True

Expand Down
1 change: 0 additions & 1 deletion requirements/tox-tests.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
treq==22.2.0
hypothesis==6.48.2
idna==3.3
12 changes: 0 additions & 12 deletions src/klein/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,3 @@
"""
Tests for L{klein}.
"""

from hypothesis import HealthCheck, settings


settings.register_profile(
"patience",
settings(
deadline=None,
suppress_health_check=[HealthCheck.too_slow],
),
)
settings.load_profile("patience")
175 changes: 175 additions & 0 deletions src/klein/test/not_hypothesis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
"""
We have had a history of U{bad
experiences<https://github.com/twisted/klein/issues/561>} with Hypothesis in
Klein, and maybe it's not actually a good application of this tool at all. As
such we have removed it, at least for now. This module presents a vaguely
Hypothesis-like stub, to keep the structure of our tests in a
Hypothesis-friendly shape, in case we want to put it back.
"""

from functools import wraps
from itertools import product
from string import ascii_uppercase
from typing import Callable, Iterable, Optional, Tuple, TypeVar

from hyperlink import DecodedURL


T = TypeVar("T")
S = TypeVar("S")


def given(
*args: Callable[[], Iterable[T]],
**kwargs: Callable[[], Iterable[T]],
) -> Callable[[Callable[..., None]], Callable[..., None]]:
def decorator(testMethod: Callable[..., None]) -> Callable[..., None]:
@wraps(testMethod)
def realTestMethod(self: S) -> None:
everyPossibleArgs = product(
*[eachFactory() for eachFactory in args]
)
everyPossibleKwargs = product(
*[
[(name, eachValue) for eachValue in eachFactory()]
for (name, eachFactory) in kwargs.items()
]
)
everyPossibleSignature = product(
everyPossibleArgs, everyPossibleKwargs
)
# not quite the _full_ cartesian product but the whole point is
# that we're making a feeble attempt at this rather than bringing
# in hypothesis.
for (computedArgs, computedPairs) in everyPossibleSignature:
computedKwargs = dict(computedPairs)
testMethod(self, *computedArgs, **computedKwargs)

return realTestMethod

return decorator


def binary() -> Callable[[], Iterable[bytes]]:
"""
Generate some binary data.
"""

def params() -> Iterable[bytes]:
return [b"data", b"data data data", b"\x00" * 50, b""]

return params


def ascii_text(min_size: int) -> Callable[[], Iterable[str]]:
"""
Generate some ASCII strs.
"""

def params() -> Iterable[str]:
yield from [
"ascii-text",
"some more ascii text",
]
assert min_size, "nothing needs 0-length strings right now"

return params


def latin1_text(min_size: int = 0) -> Callable[[], Iterable[str]]:
"""
Generate some strings encodable as latin1
"""

def params() -> Iterable[str]:
yield from [
"latin1-text",
"some more latin1 text",
"hére is latin1 text",
]
if not min_size:
yield ""

return params


def text(
min_size: int = 0, alphabet: Optional[str] = None
) -> Callable[[], Iterable[str]]:
"""
Generate some text.
"""

def params() -> Iterable[str]:
if alphabet == ascii_uppercase:
yield from ascii_text(min_size)()
return
yield from latin1_text(min_size)()
yield "\N{SNOWMAN}"

return params


def textHeaderPairs() -> Callable[[], Iterable[Iterable[Tuple[str, str]]]]:
"""
Generate some pairs of headers with text values.
"""

def params() -> Iterable[Iterable[Tuple[str, str]]]:
return [[], [("text", "header")]]

return params


def bytesHeaderPairs() -> Callable[[], Iterable[Iterable[Tuple[str, bytes]]]]:
"""
Generate some pairs of headers with bytes values.
"""

def params() -> Iterable[Iterable[Tuple[str, bytes]]]:
return [[], [("bytes", b"header")]]

return params


def booleans() -> Callable[[], Iterable[bool]]:
def parameters() -> Iterable[bool]:
yield True
yield False

return parameters


def jsonObjects() -> Callable[[], Iterable[object]]:
def parameters() -> Iterable[object]:
yield {}
yield {"hello": "world"}
yield {"here is": {"some": "nesting"}}
yield {
"and": "multiple",
"keys": {
"with": "nesting",
"and": 1234,
"numbers": ["with", "lists", "too"],
"also": ("tuples", "can", "serialize"),
},
}

return parameters


def decoded_urls() -> Callable[[], Iterable[DecodedURL]]:
"""
Generate a few URLs U{with only path and domain names
<https://github.com/python-hyper/hyperlink/issues/181>} kind of like
Hyperlink's own hypothesis strategy.
"""

def parameters() -> Iterable[DecodedURL]:
yield DecodedURL.from_text("https://example.com/")
yield DecodedURL.from_text("https://example.com")
yield DecodedURL.from_text("http://example.com/")
yield DecodedURL.from_text("https://example.com/é")
yield DecodedURL.from_text("https://súbdomain.example.com/ascii/path/")

return parameters
73 changes: 10 additions & 63 deletions src/klein/test/test_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from abc import ABC, abstractmethod
from collections import defaultdict
from string import ascii_letters
from typing import (
AnyStr,
Callable,
Expand All @@ -20,17 +19,6 @@
cast,
)

from hypothesis import given
from hypothesis.strategies import (
binary,
characters,
composite,
iterables,
lists,
text,
tuples,
)

from .._headers import (
HEADER_NAME_ENCODING,
HEADER_VALUE_ENCODING,
Expand All @@ -49,6 +37,14 @@
normalizeRawHeadersFrozen,
)
from ._trial import TestCase
from .not_hypothesis import (
binary,
bytesHeaderPairs,
given,
latin1_text,
text,
textHeaderPairs,
)


__all__ = ()
Expand All @@ -58,55 +54,6 @@
DrawCallable = Callable[[Callable[..., T]], T]


@composite
def ascii_text(
draw: DrawCallable,
min_size: Optional[int] = 0,
max_size: Optional[int] = None,
) -> str: # pragma: no cover
"""
A strategy which generates ASCII-encodable text.
@param min_size: The minimum number of characters in the text.
C{None} is treated as C{0}.
@param max_size: The maximum number of characters in the text.
Use C{None} for an unbounded size.
"""
return cast(
str,
draw(
text(min_size=min_size, max_size=max_size, alphabet=ascii_letters)
),
)


@composite # pragma: no cover
def latin1_text(
draw: DrawCallable,
min_size: Optional[int] = 0,
max_size: Optional[int] = None,
) -> str:
"""
A strategy which generates ISO-8859-1-encodable text.
@param min_size: The minimum number of characters in the text.
C{None} is treated as C{0}.
@param max_size: The maximum number of characters in the text.
Use C{None} for an unbounded size.
"""
return "".join(
draw(
lists(
characters(max_codepoint=255),
min_size=min_size,
max_size=max_size,
)
)
)


def encodeName(name: str) -> Optional[bytes]:
return name.encode(HEADER_NAME_ENCODING)

Expand Down Expand Up @@ -304,7 +251,7 @@ def headerNormalize(self, value: str) -> str:
"""
return value

@given(iterables(tuples(ascii_text(min_size=1), latin1_text())))
@given(textHeaderPairs())
def test_getTextName(self, textPairs: Iterable[Tuple[str, str]]) -> None:
"""
C{getValues} returns an iterable of L{str} values for
Expand Down Expand Up @@ -333,7 +280,7 @@ def test_getTextName(self, textPairs: Iterable[Tuple[str, str]]) -> None:
f"header name: {name!r}",
)

@given(iterables(tuples(ascii_text(min_size=1), binary())))
@given(bytesHeaderPairs())
def test_getTextNameBinaryValues(
self, pairs: Iterable[Tuple[str, bytes]]
) -> None:
Expand Down
4 changes: 1 addition & 3 deletions src/klein/test/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
from abc import ABC, abstractmethod
from typing import cast

from hypothesis import given
from hypothesis.strategies import binary

from twisted.internet.defer import ensureDeferred

from .._imessage import IHTTPMessage
from .._message import FountAlreadyAccessedError, bytesToFount, fountToBytes
from ._trial import TestCase
from .not_hypothesis import binary, given


__all__ = ()
Expand Down
Loading

0 comments on commit 9d91f10

Please sign in to comment.