Skip to content

Commit 918448a

Browse files
authored
Supporting Python 3.7 (#97)
* Trying to make Python 3.7 happy. Enabling it on Travis. * Removing generated files for Shippable. * All tests are passing with Python 3.7! * Mocket now supports Python 3.7 [skip ci].
1 parent b4b06a7 commit 918448a

File tree

8 files changed

+67
-18
lines changed

8 files changed

+67
-18
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
dist: xenial
12
services:
23
- redis-server
34
language: python
@@ -6,9 +7,9 @@ python:
67
- "3.4"
78
- "3.5"
89
- "3.6"
9-
# - "3.7-dev" # need to investigate the failure
10+
- "3.7"
1011
- "pypy"
11-
# - "pypy3"
12+
- "pypy3"
1213
install:
1314
- make develop
1415
script:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ publish:
3838
anaconda upload dist/mocket-$(shell python -c 'import mocket; print(mocket.__version__)').tar.gz
3939

4040
clean:
41-
rm -rf dist
41+
rm -rf dist shippable
4242
rm -rf *.egg-info
4343
find . -type d -name __pycache__ -exec rm -rf {} \;
4444

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ A socket mock framework
2020
-------------------------
2121
for all kinds of socket *animals*, web-clients included - with gevent/asyncio/SSL support
2222

23+
Versioning
24+
==========
25+
Starting from 3.7.0, Mocket major version will follow the same numbering pattern as Python's and therefore indicate the most recent Python version that is supported.
26+
2327
Support it
2428
==========
2529
*Star* the project on GitHub, Buy Me a Coffee or, even better, contribute with patches or documentation.

mocket/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77

88
__all__ = (mocketize, Mocket, MocketEntry, Mocketizer)
99

10-
__version__ = '2.7.4'
10+
__version__ = "3.7.0"

mocket/mocket.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import decorator
1414
import hexdump
15+
import urllib3
16+
from urllib3.util.ssl_ import ssl_wrap_socket as urllib3_ssl_wrap_socket
17+
from urllib3.util.ssl_ import wrap_socket as urllib3_wrap_socket
1518

1619
from .compat import (
1720
FileNotFoundError,
@@ -22,7 +25,7 @@
2225
encode_to_bytes,
2326
text_type,
2427
)
25-
from .utils import MocketSocketCore, wrap_ssl_socket, SSL_PROTOCOL
28+
from .utils import SSL_PROTOCOL, MocketSocketCore, wrap_ssl_socket
2629

2730
xxh32 = None
2831
try:
@@ -41,6 +44,7 @@
4144
except ImportError:
4245
pyopenssl_override = False
4346

47+
4448
true_socket = socket.socket
4549
true_create_connection = socket.create_connection
4650
true_gethostbyname = socket.gethostbyname
@@ -50,6 +54,8 @@
5054
true_ssl_socket = ssl.SSLSocket
5155
true_ssl_context = ssl.SSLContext
5256
true_inet_pton = socket.inet_pton
57+
true_urllib3_wrap_socket = urllib3_wrap_socket
58+
true_urllib3_ssl_wrap_socket = urllib3_ssl_wrap_socket
5359

5460

5561
class SuperFakeSSLContext(object):
@@ -87,6 +93,8 @@ def load_default_certs(*args, **kwargs):
8793

8894
@staticmethod
8995
def wrap_socket(sock=sock, *args, **kwargs):
96+
sock.kwargs = kwargs
97+
sock._secure_socket = True
9098
return sock
9199

92100
def wrap_bio(self, incoming, outcoming, *args, **kwargs):
@@ -126,6 +134,7 @@ class MocketSocket(object):
126134
compression = lambda s: ssl.OP_NO_COMPRESSION
127135
_mode = None
128136
_bufsize = None
137+
_secure_socket = False
129138

130139
def __init__(
131140
self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *args, **kwargs
@@ -139,6 +148,7 @@ def __init__(
139148
self.type = int(type)
140149
self.proto = int(proto)
141150
self._truesocket_recording_dir = None
151+
self.kwargs = kwargs
142152

143153
sock = kwargs.get("sock")
144154
if sock is not None:
@@ -175,6 +185,9 @@ def settimeout(self, timeout):
175185
except AttributeError:
176186
pass
177187

188+
def getsockopt(self, level, optname, buflen=None):
189+
return socket.SOCK_STREAM
190+
178191
def do_handshake(self):
179192
pass
180193

@@ -309,6 +322,18 @@ def true_sendall(self, data, *args, **kwargs):
309322
host, port = Mocket._address
310323
host = true_gethostbyname(host)
311324

325+
if isinstance(self.true_socket, true_socket) and self._secure_socket:
326+
try:
327+
self = MocketSocket(sock=self)
328+
except TypeError:
329+
ssl_context = self.kwargs.get("ssl_context")
330+
server_hostname = self.kwargs.get("server_hostname")
331+
self.true_socket = true_ssl_context.wrap_socket(
332+
self=ssl_context,
333+
sock=self.true_socket,
334+
server_hostname=server_hostname,
335+
)
336+
312337
try:
313338
self.true_socket.connect((host, port))
314339
except (OSError, socket.error, ValueError):
@@ -342,7 +367,7 @@ def true_sendall(self, data, *args, **kwargs):
342367
)
343368
)
344369

345-
# response back to .sendall() which writes it to the mocket socket and flush the BytesIO
370+
# response back to .sendall() which writes it to the Mocket socket and flush the BytesIO
346371
return encoded_response
347372

348373
def send(self, data, *args, **kwargs): # pragma: no cover
@@ -438,11 +463,20 @@ def enable(namespace=None, truesocket_recording_dir=None):
438463
(2, 1, 6, "", (host, port))
439464
]
440465
ssl.wrap_socket = ssl.__dict__["wrap_socket"] = FakeSSLContext.wrap_socket
441-
ssl.SSLSocket = ssl.__dict__["SSLSocket"] = MocketSocket
466+
# ssl.SSLSocket = ssl.__dict__["SSLSocket"] = MocketSocket
442467
ssl.SSLContext = ssl.__dict__["SSLContext"] = FakeSSLContext
443468
socket.inet_pton = socket.__dict__["inet_pton"] = lambda family, ip: byte_type(
444469
"\x7f\x00\x00\x01", "utf-8"
445470
)
471+
urllib3.util.ssl_.wrap_socket = urllib3.util.ssl_.__dict__[
472+
"wrap_socket"
473+
] = FakeSSLContext.wrap_socket
474+
urllib3.util.ssl_.ssl_wrap_socket = urllib3.util.ssl_.__dict__[
475+
"ssl_wrap_socket"
476+
] = FakeSSLContext.wrap_socket
477+
urllib3.connection.ssl_wrap_socket = urllib3.connection.__dict__[
478+
"ssl_wrap_socket"
479+
] = FakeSSLContext.wrap_socket
446480
if pyopenssl_override:
447481
# Take out the pyopenssl version - use the default implementation
448482
extract_from_urllib3()
@@ -459,9 +493,18 @@ def disable():
459493
socket.gethostbyname = socket.__dict__["gethostbyname"] = true_gethostbyname
460494
socket.getaddrinfo = socket.__dict__["getaddrinfo"] = true_getaddrinfo
461495
ssl.wrap_socket = ssl.__dict__["wrap_socket"] = true_ssl_wrap_socket
462-
ssl.SSLSocket = ssl.__dict__["SSLSocket"] = true_ssl_socket
496+
# ssl.SSLSocket = ssl.__dict__["SSLSocket"] = true_ssl_socket
463497
ssl.SSLContext = ssl.__dict__["SSLContext"] = true_ssl_context
464498
socket.inet_pton = socket.__dict__["inet_pton"] = true_inet_pton
499+
urllib3.util.ssl_.wrap_socket = urllib3.util.ssl_.__dict__[
500+
"wrap_socket"
501+
] = true_urllib3_wrap_socket
502+
urllib3.util.ssl_.ssl_wrap_socket = urllib3.util.ssl_.__dict__[
503+
"ssl_wrap_socket"
504+
] = true_urllib3_ssl_wrap_socket
505+
urllib3.connection.ssl_wrap_socket = urllib3.connection.__dict__[
506+
"ssl_wrap_socket"
507+
] = true_urllib3_ssl_wrap_socket
465508
Mocket.reset()
466509
if pyopenssl_override:
467510
# Put the pyopenssl version back in place

shippable.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ python:
55
- 3.4
66
- 3.5
77
- 3.6
8-
#- 3.7
8+
- 3.7
99
- pypy
1010
- pypy3
1111

tests/main/test_http.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,8 @@ def test_same_url_different_methods(self):
269269

270270
methods_from_responses = [r["method"] for r in responses]
271271
contents_from_responses = [r["content"] for r in responses]
272-
self.assertEquals(methods, methods_from_responses)
273-
self.assertEquals(list(range(len(methods))), contents_from_responses)
272+
self.assertEqual(methods, methods_from_responses)
273+
self.assertEqual(list(range(len(methods))), contents_from_responses)
274274

275275
@mocketize
276276
def test_request_bodies(self):

tests/main/test_https.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ def test_json(response):
4343
@pytest.mark.skipif('os.getenv("SKIP_TRUE_HTTP", False)')
4444
@mocketize(truesocket_recording_dir=recording_directory)
4545
def test_truesendall_with_recording_https():
46-
url = 'https://httpbin.org/ip'
46+
url = 'https://mockbin.com/ip'
4747

48-
requests.get(url)
49-
resp = requests.get(url)
48+
requests.get(url, headers={"Accept": "application/json"})
49+
resp = requests.get(url, headers={"Accept": "application/json"})
50+
print(resp.content)
5051
assert resp.status_code == 200
5152

5253
dump_filename = os.path.join(
@@ -56,15 +57,15 @@ def test_truesendall_with_recording_https():
5657
with io.open(dump_filename) as f:
5758
responses = json.load(f)
5859

59-
assert len(responses['httpbin.org']['443'].keys()) == 1
60+
assert len(responses['mockbin.com']['443'].keys()) == 1
6061

6162

6263
@pytest.mark.skipif('os.getenv("SKIP_TRUE_HTTP", False)')
6364
def test_truesendall_after_mocket_session():
6465
Mocket.enable()
6566
Mocket.disable()
6667

67-
url = 'https://httpbin.org/ip'
68+
url = 'https://mockbin.com/ip'
6869
resp = requests.get(url)
6970
assert resp.status_code == 200
7071

@@ -73,8 +74,8 @@ def test_truesendall_after_mocket_session():
7374
def test_real_request_session():
7475
session = requests.Session()
7576

76-
url1 = 'https://httpbin.org/ip'
77-
url2 = 'https://httpbin.org/headers'
77+
url1 = 'https://mockbin.com/ip'
78+
url2 = 'http://mockbin.com/request'
7879

7980
with Mocketizer():
8081
assert len(session.get(url1).content) < len(session.get(url2).content)

0 commit comments

Comments
 (0)