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

refactor: rework Entry, Request and Response classes #276

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f619f9e
chore: disable doctest
betaboon Dec 1, 2024
8ebb819
refactor: move mocket.compat to mocket.core.compat
betaboon Dec 1, 2024
5837d0d
refactor: move mocket.socket to mocket.core.socket
betaboon Dec 1, 2024
171d678
refactor: move mocket.ssl to mocket.core.ssl
betaboon Dec 1, 2024
b29fc99
refactor: move mocket.async_mocket to mocket.core.async_mocket
betaboon Dec 1, 2024
9fc2041
refactor: move mocket.exceptions to mocket.core.exceptions
betaboon Dec 1, 2024
e2d7636
refactor: move mocket.inject to mocket.core.inject
betaboon Dec 1, 2024
bdd3e1a
refactor: move mocket.io to mocket.core.io
betaboon Dec 1, 2024
924879c
refactor: move mocket.mocket to mocket.core.mocket
betaboon Dec 1, 2024
13b3396
refactor: move mocket.mocketizer to mocket.core.mocketizer
betaboon Dec 1, 2024
f9edaca
refactor: move mocket.mode to mocket.core.mode
betaboon Dec 1, 2024
c82c214
refactor: move mocket.recording to mocket.core.recording
betaboon Dec 1, 2024
8501efa
refactor: move mocket.types to mocket.core.types
betaboon Dec 1, 2024
e6043a7
refactor: move mocket.urllib3 to mocket.core.urllib3
betaboon Dec 1, 2024
f2847a8
refactor: move mocket.utils to mocket.core.utils
betaboon Dec 1, 2024
ac722a3
refactor: move mocket.entry to mocket.compat.entry
betaboon Dec 1, 2024
3ebf42b
refactor: move mocket.mockhttp to mocket.compat.mockhttp
betaboon Dec 1, 2024
26780c2
refactor: move mocket.mockredis to mocket.compat.mockredis
betaboon Dec 1, 2024
9c7c400
chore: mocket.core.compat - add type annotations
betaboon Dec 1, 2024
f264359
chore: mocket.core.io - add type annotations
betaboon Dec 1, 2024
0cda606
feat: introduce new mocket.core.entry
betaboon Dec 1, 2024
d2a1288
feat: introduce new mocket.bytes
betaboon Dec 1, 2024
5eb9c0c
feat: introduce new mocket.redis
betaboon Dec 1, 2024
fcb861a
feat: introduce new mocket.http
betaboon Dec 1, 2024
fd2edda
refactor: migrate mocket.compat.mockredis to use mocket.redis
betaboon Dec 1, 2024
c29442d
refactor: migrate mocket.plugins.httpretty to use mocket.http
betaboon Dec 1, 2024
69fc895
refactor: migrate mocket.plugins.pook_mock_engine to use mocket.http
betaboon Dec 2, 2024
dc0e2d9
refactor: migrate mocket.compat.mockhttp to use mocket.http
betaboon Dec 1, 2024
b4e3484
refactor: migrate mocket.compat.entry to use mocket.core.entry
betaboon Dec 1, 2024
218eb32
chore: re-enable doctest
betaboon Dec 1, 2024
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
56 changes: 42 additions & 14 deletions mocket/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
from mocket.async_mocket import async_mocketize
from mocket.entry import MocketEntry
from mocket.mocket import Mocket
from mocket.mocketizer import Mocketizer, mocketize
from mocket.ssl.context import MocketSSLContext

# NOTE this is here for backwards-compat to keep old import-paths working
from mocket.ssl.context import MocketSSLContext as FakeSSLContext
from mocket.bytes import (
MocketBytesEntry,
MocketBytesRequest,
MocketBytesResponse,
)
from mocket.compat import FakeSSLContext, MocketEntry
from mocket.core.async_mocket import async_mocketize
from mocket.core.mocket import Mocket
from mocket.core.mocketizer import Mocketizer, mocketize
from mocket.core.socket import MocketSocket
from mocket.core.ssl.context import MocketSSLContext
from mocket.core.ssl.socket import MocketSSLSocket
from mocket.http import (
MocketHttpEntry,
MocketHttpMethod,
MocketHttpRequest,
MocketHttpResponse,
)
from mocket.redis import (
MocketRedisEntry,
MocketRedisRequest,
MocketRedisResponse,
)

__all__ = (
"async_mocketize",
"mocketize",
__all__ = [
"Mocket",
"MocketEntry",
"Mocketizer",
"MocketBytesEntry",
"MocketBytesRequest",
"MocketBytesResponse",
"MocketHttpEntry",
"MocketHttpMethod",
"MocketHttpRequest",
"MocketHttpResponse",
"MocketRedisEntry",
"MocketRedisRequest",
"MocketRedisResponse",
"MocketSSLContext",
"MocketSSLSocket",
"MocketSocket",
"Mocketizer",
"async_mocketize",
"mocketize",
# NOTE this is here for backwards-compat to keep old import-paths working
"FakeSSLContext",
)
"MocketEntry",
]

__version__ = "3.13.2"
26 changes: 5 additions & 21 deletions mocket/async_mocket.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
from mocket.mocketizer import Mocketizer
from mocket.utils import get_mocketize
from mocket.core.async_mocket import async_mocketize


async def wrapper(
test,
truesocket_recording_dir=None,
strict_mode=False,
strict_mode_allowed=None,
*args,
**kwargs,
):
async with Mocketizer.factory(
test, truesocket_recording_dir, strict_mode, strict_mode_allowed, args
):
return await test(*args, **kwargs)


async_mocketize = get_mocketize(wrapper_=wrapper)


__all__ = ("async_mocketize",)
# NOTE this is here for backwards-compat to keep old import-paths working
__all__ = [
"async_mocketize",
]
79 changes: 79 additions & 0 deletions mocket/bytes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from __future__ import annotations

from typing import Sequence

from typing_extensions import Self

from mocket.core.entry import MocketBaseEntry, MocketBaseRequest, MocketBaseResponse
from mocket.core.mocket import Mocket
from mocket.core.types import Address


class MocketBytesRequest(MocketBaseRequest):
def __init__(self) -> None:
self._data = b""

@property
def data(self) -> bytes:
return self._data

@classmethod
def from_data(cls: type[Self], data: bytes) -> Self:
request = cls()
request._data = data
return request


class MocketBytesResponse(MocketBaseResponse):
def __init__(self, data: bytes | str | bool) -> None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MocketBytesResponse can be created with bytes/str/bool? Isn't it confusing?

if isinstance(data, str):
data = data.encode()
elif isinstance(data, bool):
data = bytes(data)
self._data = data

@property
def data(self) -> bytes:
return self._data


class MocketBytesEntry(MocketBaseEntry):
request_cls = MocketBytesRequest
response_cls = MocketBytesResponse

def __init__(
self,
address: Address,
responses: Sequence[MocketBytesResponse | Exception],
) -> None:
if not len(responses):
responses = [MocketBytesResponse(data=b"")]

super().__init__(
address=address,
responses=responses,
)

@classmethod
def register_response(
cls,
address: Address,
response: MocketBytesResponse | Exception,
) -> None:
entry = cls(
address=address,
responses=[response],
)
Mocket.register(entry)

@classmethod
def register_responses(
cls,
address: Address,
responses: Sequence[MocketBytesResponse | Exception],
) -> None:
entry = cls(
address=address,
responses=responses,
)
Mocket.register(entry)
8 changes: 8 additions & 0 deletions mocket/compat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from mocket.compat.entry import MocketEntry, Response
from mocket.core.ssl.context import MocketSSLContext as FakeSSLContext

__all__ = [
"FakeSSLContext",
"MocketEntry",
"Response",
]
36 changes: 36 additions & 0 deletions mocket/compat/entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

from mocket.bytes import MocketBytesEntry, MocketBytesResponse
from mocket.core.types import Address


class Response(MocketBytesResponse):
def __init__(self, data: bytes | str | bool) -> None:
if isinstance(data, str):
data = data.encode()
elif isinstance(data, bool):
data = bytes(data)
self._data = data


class MocketEntry(MocketBytesEntry):
def __init__(
self,
location: Address,
responses: list[MocketBytesResponse | Exception | bytes | str | bool]
| MocketBytesResponse
| Exception
| bytes
| str
| bool,
) -> None:
if not isinstance(responses, list):
responses = [responses]

_responses = []
for response in responses:
if not isinstance(response, (MocketBytesResponse, Exception)):
response = MocketBytesResponse(response)
_responses.append(response)

super().__init__(address=location, responses=_responses)
139 changes: 139 additions & 0 deletions mocket/compat/mockhttp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from __future__ import annotations

from io import BufferedReader
from typing import Any

from mocket.http import (
MocketHttpEntry,
MocketHttpMethod,
MocketHttpRequest,
MocketHttpResponse,
)
from mocket.mocket import Mocket


class Response(MocketHttpResponse):
def __init__(
self,
body: str | bytes | BufferedReader = b"",
status: int = 200,
headers: dict[str, str] | None = None,
) -> None:
super().__init__(
status_code=status,
headers=headers,
body=body,
)

@property
def status(self) -> int:
return self.status_code


class Request(MocketHttpRequest):
@property
def body(self) -> str | None: # type: ignore
body = super().body
if body is None:
return None
return body.decode()


class Entry(MocketHttpEntry):
request_cls = Request
response_cls = Response # type: ignore[assignment]

CONNECT = MocketHttpMethod.CONNECT
DELETE = MocketHttpMethod.DELETE
GET = MocketHttpMethod.GET
HEAD = MocketHttpMethod.HEAD
OPTIONS = MocketHttpMethod.OPTIONS
PATCH = MocketHttpMethod.PATCH
POST = MocketHttpMethod.POST
PUT = MocketHttpMethod.PUT
TRACE = MocketHttpMethod.TRACE

METHODS = list(MocketHttpMethod)

def __init__(
self,
uri: str,
method: MocketHttpMethod,
responses: list[Response | Exception],
match_querystring: bool = True,
add_trailing_slash: bool = True,
) -> None:
super().__init__(
method=method,
uri=uri,
responses=responses,
match_querystring=match_querystring,
add_trailing_slash=add_trailing_slash,
)

def __repr__(self) -> str:
return (
f"{self.__class__.__name__}("
f"method='{self.method.name}', "
f"schema='{self.schema}', "
f"location={self.address}, "
f"path='{self.path}', "
f"query='{self.query}'"
")"
)

@property
def schema(self) -> str:
return self.scheme

@classmethod
def register(
cls,
method: MocketHttpMethod,
uri: str,
*responses: Response | Exception,
**config: Any,
) -> None:
if "body" in config or "status" in config:
raise AttributeError("Did you mean `Entry.single_register(...)`?")

if isinstance(config, dict):
match_querystring = config.get("match_querystring", True)
add_trailing_slash = config.get("add_trailing_slash", True)

entry = cls(
method=method,
uri=uri,
responses=list(responses),
match_querystring=match_querystring,
add_trailing_slash=add_trailing_slash,
)
Mocket.register(entry)

@classmethod
def single_register(
cls,
method: MocketHttpMethod,
uri: str,
body: str | bytes | BufferedReader = b"",
status: int = 200,
headers: dict[str, str] | None = None,
match_querystring: bool = True,
exception: Exception | None = None,
) -> None:
response: Response | Exception
if exception is not None:
response = exception
else:
response = Response(
body=body,
status=status,
headers=headers,
)

cls.register(
method,
uri,
response,
match_querystring=match_querystring,
)
Loading