Skip to content

Commit cd59dbc

Browse files
committed
refactor: introduce state-object
1 parent d6b3bb1 commit cd59dbc

13 files changed

Lines changed: 146 additions & 154 deletions

File tree

mocket/entry.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import collections.abc
22

3+
import mocket.state
34
from mocket.compat import encode_to_bytes
45

56

@@ -41,10 +42,8 @@ def can_handle(data):
4142
return True
4243

4344
def collect(self, data):
44-
from mocket import Mocket
45-
4645
req = self.request_cls(data)
47-
Mocket.collect(req)
46+
mocket.state.state.collect(req)
4847

4948
def get_response(self):
5049
response = self.responses[self.response_index]

mocket/inject.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from urllib3.connection import match_hostname as urllib3_match_hostname
99
from urllib3.util.ssl_ import ssl_wrap_socket as urllib3_ssl_wrap_socket
1010

11+
import mocket.state
12+
1113
try:
1214
from urllib3.util.ssl_ import wrap_socket as urllib3_wrap_socket
1315
except ImportError:
@@ -42,12 +44,11 @@ def enable(
4244
namespace: str | None = None,
4345
truesocket_recording_dir: str | None = None,
4446
) -> None:
45-
from mocket.mocket import Mocket
4647
from mocket.socket import MocketSocket, create_connection, socketpair
4748
from mocket.ssl import FakeSSLContext
4849

49-
Mocket._namespace = namespace
50-
Mocket._truesocket_recording_dir = truesocket_recording_dir
50+
mocket.state.state._namespace = namespace
51+
mocket.state.state._truesocket_recording_dir = truesocket_recording_dir
5152

5253
if truesocket_recording_dir and not os.path.isdir(truesocket_recording_dir):
5354
# JSON dumps will be saved here
@@ -91,8 +92,6 @@ def enable(
9192

9293

9394
def disable() -> None:
94-
from mocket.mocket import Mocket
95-
9695
socket.socket = socket.__dict__["socket"] = true_socket
9796
socket._socketobject = socket.__dict__["_socketobject"] = true_socket
9897
socket.SocketType = socket.__dict__["SocketType"] = true_socket
@@ -122,7 +121,7 @@ def disable() -> None:
122121
urllib3.connection.match_hostname = urllib3.connection.__dict__[
123122
"match_hostname"
124123
] = true_urllib3_match_hostname
125-
Mocket.reset()
124+
mocket.state.state.reset()
126125
if pyopenssl_override: # pragma: no cover
127126
# Put the pyopenssl version back in place
128127
inject_into_urllib3()

mocket/io.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import io
22
import os
33

4+
import mocket.state
5+
46

57
class MocketSocketCore(io.BytesIO):
68
def __init__(self, address) -> None:
79
self._address = address
810
super().__init__()
911

1012
def write(self, content):
11-
from mocket import Mocket
12-
1313
super().write(content)
1414

15-
_, w_fd = Mocket.get_pair(self._address)
15+
_, w_fd = mocket.state.state.get_pair(self._address)
1616
if w_fd:
1717
os.write(w_fd, content)

mocket/mocket.py

Lines changed: 9 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,18 @@
1-
import collections
2-
import itertools
3-
import os
4-
from typing import Optional, Tuple
1+
from typing import cast
52

63
import mocket.inject
4+
import mocket.state
75

86
# NOTE this is here for backwards-compat to keep old import-paths working
97
from mocket.socket import MocketSocket as MocketSocket
108

119

12-
class Mocket:
13-
_socket_pairs = {}
14-
_address = (None, None)
15-
_entries = collections.defaultdict(list)
16-
_requests = []
17-
_namespace = str(id(_entries))
18-
_truesocket_recording_dir = None
10+
class _Mocket(mocket.state.MocketState):
11+
def __init__(self) -> None:
12+
self.enable = mocket.inject.enable
13+
self.disable = mocket.inject.disable
1914

20-
@classmethod
21-
def get_pair(cls, address: tuple) -> Tuple[Optional[int], Optional[int]]:
22-
"""
23-
Given the id() of the caller, return a pair of file descriptors
24-
as a tuple of two integers: (<read_fd>, <write_fd>)
25-
"""
26-
return cls._socket_pairs.get(address, (None, None))
2715

28-
@classmethod
29-
def set_pair(cls, address: tuple, pair: Tuple[int, int]) -> None:
30-
"""
31-
Store a pair of file descriptors under the key `id_`
32-
as a tuple of two integers: (<read_fd>, <write_fd>)
33-
"""
34-
cls._socket_pairs[address] = pair
35-
36-
@classmethod
37-
def register(cls, *entries):
38-
for entry in entries:
39-
cls._entries[entry.location].append(entry)
40-
41-
@classmethod
42-
def get_entry(cls, host, port, data):
43-
host = host or Mocket._address[0]
44-
port = port or Mocket._address[1]
45-
entries = cls._entries.get((host, port), [])
46-
for entry in entries:
47-
if entry.can_handle(data):
48-
return entry
49-
50-
@classmethod
51-
def collect(cls, data):
52-
cls.request_list().append(data)
53-
54-
@classmethod
55-
def reset(cls):
56-
for r_fd, w_fd in cls._socket_pairs.values():
57-
os.close(r_fd)
58-
os.close(w_fd)
59-
cls._socket_pairs = {}
60-
cls._entries = collections.defaultdict(list)
61-
cls._requests = []
62-
63-
@classmethod
64-
def last_request(cls):
65-
if cls.has_requests():
66-
return cls.request_list()[-1]
67-
68-
@classmethod
69-
def request_list(cls):
70-
return cls._requests
71-
72-
@classmethod
73-
def remove_last_request(cls):
74-
if cls.has_requests():
75-
del cls._requests[-1]
76-
77-
@classmethod
78-
def has_requests(cls):
79-
return bool(cls.request_list())
80-
81-
@classmethod
82-
def get_namespace(cls):
83-
return cls._namespace
84-
85-
@staticmethod
86-
def enable(namespace=None, truesocket_recording_dir=None):
87-
mocket.inject.enable(namespace, truesocket_recording_dir)
88-
89-
@staticmethod
90-
def disable():
91-
mocket.inject.disable()
92-
93-
@classmethod
94-
def get_truesocket_recording_dir(cls):
95-
return cls._truesocket_recording_dir
96-
97-
@classmethod
98-
def assert_fail_if_entries_not_served(cls):
99-
"""Mocket checks that all entries have been served at least once."""
100-
if not all(entry._served for entry in itertools.chain(*cls._entries.values())):
101-
raise AssertionError("Some Mocket entries have not been served")
16+
Mocket = cast(_Mocket, mocket.state.state)
17+
Mocket.enable = mocket.inject.enable
18+
Mocket.disable = mocket.inject.disable

mocket/mocketizer.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import mocket.inject
12
from mocket.mode import MocketMode
23
from mocket.utils import get_mocketize
34

@@ -23,9 +24,7 @@ def __init__(
2324
)
2425

2526
def enter(self):
26-
from mocket import Mocket
27-
28-
Mocket.enable(
27+
mocket.inject.enable(
2928
namespace=self.namespace,
3029
truesocket_recording_dir=self.truesocket_recording_dir,
3130
)
@@ -39,9 +38,8 @@ def __enter__(self):
3938
def exit(self):
4039
if self.instance:
4140
self.check_and_call("mocketize_teardown")
42-
from mocket import Mocket
4341

44-
Mocket.disable()
42+
mocket.inject.disable()
4543

4644
def __exit__(self, type, value, tb):
4745
self.exit()

mocket/mockhttp.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from h11 import SERVER, Connection, Data
88
from h11 import Request as H11Request
99

10+
import mocket.state
1011
from mocket.compat import ENCODING, decode_from_bytes, do_the_magic, encode_to_bytes
1112
from mocket.entry import MocketEntry
12-
from mocket.mocket import Mocket
1313

1414
STATUS = {k: v[0] for k, v in BaseHTTPRequestHandler.responses.items()}
1515
CRLF = "\r\n"
@@ -165,7 +165,7 @@ def collect(self, data):
165165

166166
decoded_data = decode_from_bytes(data)
167167
if not decoded_data.startswith(Entry.METHODS):
168-
Mocket.remove_last_request()
168+
mocket.state.state.remove_last_request()
169169
self._sent_data += data
170170
consume_response = False
171171
else:
@@ -188,7 +188,7 @@ def can_handle(self, data):
188188
requestline, _ = decode_from_bytes(data).split(CRLF, 1)
189189
method, path, _ = self._parse_requestline(requestline)
190190
except ValueError:
191-
return self is getattr(Mocket, "_last_entry", None)
191+
return self is getattr(mocket.state.state, "_last_entry", None)
192192

193193
uri = urlsplit(path)
194194
can_handle = uri.path == self.path and method == self.method
@@ -198,7 +198,7 @@ def can_handle(self, data):
198198
self.query, **kw
199199
)
200200
if can_handle:
201-
Mocket._last_entry = self
201+
mocket.state.state._last_entry = self
202202
return can_handle
203203

204204
@staticmethod
@@ -234,7 +234,7 @@ def register(cls, method, uri, *responses, **config):
234234
if config["add_trailing_slash"] and not urlsplit(uri).path:
235235
uri += "/"
236236

237-
Mocket.register(
237+
mocket.state.state.register(
238238
cls(uri, method, responses, match_querystring=config["match_querystring"])
239239
)
240240

mocket/mockredis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from itertools import chain
22

3+
import mocket.state
34
from mocket.compat import (
45
decode_from_bytes,
56
encode_to_bytes,
67
shsplit,
78
)
89
from mocket.entry import MocketEntry
9-
from mocket.mocket import Mocket
1010

1111

1212
class Request:
@@ -80,7 +80,7 @@ def register(cls, addr, command, *responses):
8080
r if isinstance(r, BaseException) else cls.response_cls(r)
8181
for r in responses
8282
]
83-
Mocket.register(cls(addr, command, responses))
83+
mocket.state.state.register(cls(addr, command, responses))
8484

8585
@classmethod
8686
def register_response(cls, command, response, addr=None):

mocket/mode.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import TYPE_CHECKING, Any, ClassVar
44

5+
import mocket.state
56
from mocket.exceptions import StrictMocketException
67

78
if TYPE_CHECKING: # pragma: no cover
@@ -31,11 +32,9 @@ def is_allowed(self, location: str | tuple[str, int]) -> bool:
3132

3233
@staticmethod
3334
def raise_not_allowed() -> NoReturn:
34-
from mocket.mocket import Mocket
35-
3635
current_entries = [
3736
(location, "\n ".join(map(str, entries)))
38-
for location, entries in Mocket._entries.items()
37+
for location, entries in mocket.state.state._entries.items()
3938
]
4039
formatted_entries = "\n".join(
4140
[f" {location}:\n {entries}" for location, entries in current_entries]

mocket/plugins/httpretty/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from mocket import Mocket, mocketize
1+
import mocket.inject
2+
import mocket.state
3+
from mocket import mocketize
24
from mocket.async_mocket import async_mocketize
35
from mocket.compat import ENCODING
46
from mocket.mockhttp import Entry as MocketHttpEntry
@@ -45,9 +47,9 @@ class Entry(MocketHttpEntry):
4547
httprettified = mocketize
4648
async_httprettified = async_mocketize
4749

48-
enable = Mocket.enable
49-
disable = Mocket.disable
50-
reset = Mocket.reset
50+
enable = mocket.inject.enable
51+
disable = mocket.inject.disable
52+
reset = mocket.state.state.reset
5153

5254
GET = Entry.GET
5355
PUT = Entry.PUT
@@ -102,9 +104,9 @@ class MocketHTTPretty:
102104

103105
def __getattr__(self, name):
104106
if name == "last_request":
105-
return Mocket.last_request()
107+
return mocket.state.state.last_request()
106108
if name == "latest_requests":
107-
return Mocket.request_list()
109+
return mocket.state.state.request_list()
108110
return getattr(Entry, name)
109111

110112

mocket/plugins/pook_mock_engine.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
except ModuleNotFoundError:
44
MockEngine = object
55

6-
from mocket.mocket import Mocket
6+
import mocket.inject
7+
import mocket.state
78
from mocket.mockhttp import Entry, Response
89

910

@@ -35,7 +36,7 @@ def single_register(
3536
[Response(body=body, status=status, headers=headers)],
3637
match_querystring=match_querystring,
3738
)
38-
Mocket.register(entry)
39+
mocket.state.state.register(entry)
3940
return entry
4041

4142

@@ -64,12 +65,12 @@ def mocket_mock_fun(*args, **kwargs):
6465
class MocketInterceptor(BaseInterceptor):
6566
@staticmethod
6667
def activate():
67-
Mocket.disable()
68-
Mocket.enable()
68+
mocket.inject.disable()
69+
mocket.inject.enable()
6970

7071
@staticmethod
7172
def disable():
72-
Mocket.disable()
73+
mocket.inject.disable()
7374

7475
# Store plugins engine
7576
self.engine = engine

0 commit comments

Comments
 (0)