Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Deprecate non-binary bodies" #131

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions History.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
History
=======

v1.5.0 / 2024-06-17
-------------------

* Provide fix for inaccurate non-binary response bodies from urllib and deprecate the `binary` body parameter entirely
* Refer to https://github.com/h2non/pook/issues/128 for further details
* Fix body matcher binary argument not passing through to `BodyMatcher`

v1.4.3 / 2024-02-23
-------------------

Expand Down
3 changes: 1 addition & 2 deletions src/pook/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
from .mock import Mock
from .mock_engine import MockEngine
from .request import Request
from .response import Response, apply_binary_body_fix
from .response import Response

from asyncio import iscoroutinefunction
from .activate_async import activate_async

# Public API symbols to export
__all__ = (
"activate",
"apply_binary_body_fix",
"on",
"disable",
"off",
Expand Down
6 changes: 1 addition & 5 deletions src/pook/interceptors/http.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import socket
from ..request import Request
from ..response import _BinaryDefault
from .base import BaseInterceptor

from unittest import mock
Expand Down Expand Up @@ -39,9 +38,6 @@ def close(self, *args, **kw):
pass


_body_fallback = b"" if _BinaryDefault.fixed else ""


class HTTPClientInterceptor(BaseInterceptor):
"""
urllib / http.client HTTP traffic interceptor.
Expand Down Expand Up @@ -96,7 +92,7 @@ def getresponse():

# Path reader
def read():
return res._body or _body_fallback
return res._body or ""

mockres.read = read

Expand Down
4 changes: 1 addition & 3 deletions src/pook/interceptors/urllib3.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,8 @@ def _on_request(

if is_chunked_response(headers):
body_chunks = body if isinstance(body, list) else [body]

body_chunks = [
chunk.encode() if callable(getattr(chunk, "encode", None)) else chunk
for chunk in body_chunks
chunk if res._binary else chunk.encode() for chunk in body_chunks
]

body = ClientHTTPResponse(MockSock)
Expand Down
2 changes: 1 addition & 1 deletion src/pook/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ def body(self, body, binary=False):
self: current Mock instance.
"""
self._request.body = body
self.add_matcher(matcher("BodyMatcher", body, binary=binary))
self.add_matcher(matcher("BodyMatcher", body, binary=False))
return self

def json(self, json):
Expand Down
96 changes: 1 addition & 95 deletions src/pook/response.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,9 @@
import json
import warnings
from textwrap import dedent

from .headers import HTTPHeaderDict
from .helpers import trigger_methods
from .constants import TYPES


class _BinaryDefault:
fixed = False

start_warning = " ".join(
dedent(
"""
Non-binary pook response bodies are deprecated.
Support for them will be removed in the next major version of pook,
and responses will always be bytes, matching the behaviour of all
client libraries that pook supports.
"""
)
.strip()
.split("\n")
)

end_warning = " ".join(
dedent(
"""
Refer to https://github.com/h2non/pook/issues/128 for further details.
In most circumstances, your existing code will continue to work without changes.
If you do not use pook to mock urllib (e.g., `urlopen` or a library that wraps it),
then you are not affected and can safely ignore this warning.
"""
)
.strip()
.split("\n")
)

@classmethod
def make_warning(cls, text):
return f"{cls.start_warning} {text} {cls.end_warning}"


def apply_binary_body_fix():
_BinaryDefault.fixed = True


class Response(object):
"""
Response is used to declare and compose an HTTP mock responses fields.
Expand Down Expand Up @@ -199,7 +158,7 @@ def content(self, name):
self._headers["Content-Type"] = TYPES.get(name, name)
return self

def body(self, body, binary=_BinaryDefault, chunked=False):
def body(self, body, binary=False, chunked=False):
"""
Defines response body data.

Expand All @@ -211,59 +170,6 @@ def body(self, body, binary=_BinaryDefault, chunked=False):
Returns:
self: ``pook.Response`` current instance.
"""
apply_fix = False
if binary is _BinaryDefault:
if _BinaryDefault.fixed:
# Fixed and not explicitly passing binary, perfect! Ready for the future!
apply_fix = True
else:
warnings.warn(
_BinaryDefault.make_warning(
"Call `pook.apply_binary_body_fix()` at least once to resolve this notice."
),
DeprecationWarning,
stacklevel=2,
)
binary = False

else: # explicitly True or False
if _BinaryDefault.fixed:
# Fixed, but explicitly passing `binary`
warnings.warn(
_BinaryDefault.make_warning(
"The fix is already applied, but `binary` was explicitly passed to `.body()`. "
"Remove `binary` from this body call, and update any application code that treated "
"the response as anything other than bytes."
),
DeprecationWarning,
stacklevel=2,
)
if binary:
apply_fix = True
else:
# Not fixed and explicitly passing `binary`
warnings.warn(
_BinaryDefault.make_warning(
"To resolve this notice, call `pook.apply_binary_body_fix()` in your "
"conftest (or equivalent). "
"Then, remove `binary` from this body call, and update any application code that treated "
"the response as anything other than bytes."
),
DeprecationWarning,
stacklevel=2,
)

if apply_fix:
binary = True

if isinstance(body, list):
for i, chunk in enumerate(body):
if hasattr(chunk, "encode"):
body[i] = chunk.encode()

if hasattr(body, "encode"):
body = body.encode()

if isinstance(body, bytes) and not binary:
body = body.decode("utf-8")

Expand Down
39 changes: 0 additions & 39 deletions tests/conftest.py

This file was deleted.

12 changes: 2 additions & 10 deletions tests/unit/interceptors/aiohttp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async def amake_request(self, method, url):
async with aiohttp.ClientSession(loop=self.loop) as session:
req = await session.request(method=method, url=url)
content = await req.read()
return req.status, content
return req.status, content.decode("utf-8")


binary_file = (Path(__file__).parents[1] / "fixtures" / "nothing.bin").read_bytes()
Expand Down Expand Up @@ -53,17 +53,9 @@ async def test_await_request(URL):
assert len(mock.matches) == 1


@pytest.mark.asyncio
async def test_binary_body_deprecated(URL, without_binary_body_fix):
pook.get(URL).reply(200).body(binary_file, binary=True)
async with aiohttp.ClientSession() as session:
req = await session.get(URL)
assert await req.read() == binary_file


@pytest.mark.asyncio
async def test_binary_body(URL):
pook.get(URL).reply(200).body(binary_file)
pook.get(URL).reply(200).body(binary_file, binary=True)
async with aiohttp.ClientSession() as session:
req = await session.get(URL)
assert await req.read() == binary_file
28 changes: 4 additions & 24 deletions tests/unit/interceptors/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import asyncio
from typing import Optional, Tuple, Union
from typing import Optional, Tuple

import pytest

Expand All @@ -8,18 +8,13 @@

class StandardTests:
is_async: bool = False
requires_binary_body_fix: bool = False

async def amake_request(
self, method: str, url: str
) -> Tuple[int, Optional[Union[str, bytes]]]:
async def amake_request(self, method: str, url: str) -> Tuple[int, Optional[str]]:
raise NotImplementedError(
"Sub-classes for async transports must implement `amake_request`"
)

def make_request(
self, method: str, url: str
) -> Tuple[int, Optional[Union[str, bytes]]]:
def make_request(self, method: str, url: str) -> Tuple[int, Optional[str]]:
if self.is_async:
return self.loop.run_until_complete(self.amake_request(method, url))

Expand All @@ -42,7 +37,7 @@ def test_activate_deactivate(self, httpbin):
status, body = self.make_request("GET", url)

assert status == 200
assert body == b"hello from pook"
assert body == "hello from pook"

pook.disable()

Expand All @@ -61,18 +56,3 @@ def test_network_mode(self, httpbin):
status, body = self.make_request("POST", upstream_url)

assert status == 500

@pytest.mark.pook
def test_binary_body_deprecated(self, httpbin, without_binary_body_fix):
url = f"{httpbin.url}/status/404"

pook.get(url).reply(200).body("hello from pook")

status, body = self.make_request("GET", url)

assert status == 200

if self.requires_binary_body_fix:
assert body == "hello from pook"
else:
assert body == b"hello from pook"
4 changes: 2 additions & 2 deletions tests/unit/interceptors/httpx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ async def amake_request(self, method, url):
async with httpx.AsyncClient() as client:
response = await client.request(method=method, url=url)
content = await response.aread()
return response.status_code, content
return response.status_code, content.decode("utf-8")


class TestStandardSyncHttpx(StandardTests):
def make_request(self, method, url):
response = httpx.request(method=method, url=url)
content = response.read()
return response.status_code, content
return response.status_code, content.decode("utf-8")


@pytest.fixture
Expand Down
Loading
Loading