From e305db980a03add207375bd7afe1bd86e5ade4cd Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Wed, 13 Dec 2017 00:28:14 +0100 Subject: [PATCH 01/15] Bugfix for HTTPS. --- mocket/__init__.py | 2 +- mocket/mocket.py | 16 ++++++++++------ tests/main/test_https.py | 9 +++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mocket/__init__.py b/mocket/__init__.py index 84167c76..21167e02 100644 --- a/mocket/__init__.py +++ b/mocket/__init__.py @@ -7,4 +7,4 @@ __all__ = (mocketize, Mocket, MocketEntry) -__version__ = '2.1.1' +__version__ = '2.1.2' diff --git a/mocket/mocket.py b/mocket/mocket.py index 40d01e52..0ef5808a 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -124,7 +124,7 @@ class MocketSocket(object): _mode = None _bufsize = None - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, detach=None): + def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kwargs): self.settimeout(socket._GLOBAL_DEFAULT_TIMEOUT) self.true_socket = true_socket(family, type, proto) self.fd = MocketSocketCore() @@ -136,6 +136,10 @@ def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, deta self.proto = int(proto) self._truesocket_recording_dir = None + sock = kwargs.get('sock') + if sock is not None: + self.__dict__ = dict(sock.__dict__) + def __unicode__(self): return str(self) @@ -381,14 +385,14 @@ def enable(namespace=None, truesocket_recording_dir=None): socket.socket = socket.__dict__['socket'] = MocketSocket socket._socketobject = socket.__dict__['_socketobject'] = MocketSocket socket.SocketType = socket.__dict__['SocketType'] = MocketSocket - ssl.SSLSocket = ssl.__dict__['SSLSocket'] = MocketSocket socket.create_connection = socket.__dict__['create_connection'] = create_connection socket.gethostname = socket.__dict__['gethostname'] = lambda: 'localhost' socket.gethostbyname = socket.__dict__['gethostbyname'] = lambda host: '127.0.0.1' socket.getaddrinfo = socket.__dict__['getaddrinfo'] = \ lambda host, port, family=None, socktype=None, proto=None, flags=None: [(2, 1, 6, '', (host, port))] ssl.wrap_socket = ssl.__dict__['wrap_socket'] = FakeSSLContext.wrap_socket - ssl.SSLContext = ssl.__dict__['SSLSocket'] = FakeSSLContext + ssl.SSLSocket = ssl.__dict__['SSLSocket'] = MocketSocket + ssl.SSLContext = ssl.__dict__['SSLContext'] = FakeSSLContext socket.inet_pton = socket.__dict__['inet_pton'] = lambda family, ip: byte_type( '\x7f\x00\x00\x01', 'utf-8' @@ -403,9 +407,9 @@ def disable(): socket.gethostname = socket.__dict__['gethostname'] = true_gethostname socket.gethostbyname = socket.__dict__['gethostbyname'] = true_gethostbyname socket.getaddrinfo = socket.__dict__['getaddrinfo'] = true_getaddrinfo - ssl.wrap_socket = ssl.__dict__['SSLSocket'] = true_ssl_wrap_socket - ssl.SSLSocket = ssl.__dict__['wrap_socket'] = true_ssl_socket - ssl.SSLContext = ssl.__dict__['SSLSocket'] = true_ssl_context + ssl.wrap_socket = ssl.__dict__['wrap_socket'] = true_ssl_wrap_socket + ssl.SSLSocket = ssl.__dict__['SSLSocket'] = true_ssl_socket + ssl.SSLContext = ssl.__dict__['SSLContext'] = true_ssl_context socket.inet_pton = socket.__dict__['inet_pton'] = true_inet_pton @classmethod diff --git a/tests/main/test_https.py b/tests/main/test_https.py index 2dea55d2..197285e6 100644 --- a/tests/main/test_https.py +++ b/tests/main/test_https.py @@ -57,3 +57,12 @@ def test_truesendall_with_recording_https(): responses = json.load(f) assert len(responses['httpbin.org']['443'].keys()) == 1 + + +def test_truesendall_after_mocket_session(): + Mocket.enable() + Mocket.disable() + + url = 'https://httpbin.org/ip' + resp = requests.get(url) + assert resp.status_code == 200 From 6efc6e583a54b0d3357d02d5d5fa2b4dba6eea56 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Mon, 18 Dec 2017 17:26:57 +0100 Subject: [PATCH 02/15] Fix for SSL true socket. Almost done. --- mocket/mocket.py | 37 ++++++++++++++++++++++++------------- mocket/utils.py | 17 +++++++++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index 0ef5808a..9fee544e 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -15,6 +15,7 @@ from .utils import ( MocketSocketCore, + wrap_ssl_socket, ) from .compat import ( encode_to_bytes, @@ -127,6 +128,7 @@ class MocketSocket(object): def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kwargs): self.settimeout(socket._GLOBAL_DEFAULT_TIMEOUT) self.true_socket = true_socket(family, type, proto) + self.true_ssl_socket = wrap_ssl_socket(true_ssl_socket, true_socket(family, type, proto), true_ssl_context()) self.fd = MocketSocketCore() self._connected = False self._buflen = 65536 @@ -248,11 +250,6 @@ def recv(self, buffersize, flags=None): return os.read(Mocket.r_fd, buffersize) return self.fd.read(buffersize) - def _connect(self): # pragma: no cover - if not self._connected: - self.true_socket.connect(Mocket._address) - self._connected = True - def true_sendall(self, data, *args, **kwargs): req = decode_from_bytes(data) # make request unique again @@ -294,16 +291,30 @@ def true_sendall(self, data, *args, **kwargs): encoded_response = hexdump.restore(encode_to_bytes(response_dict['response'])) # if not available, call the real sendall except KeyError: - self._connect() + host, port = Mocket._address + host = true_gethostbyname(host) + + self.true_socket.connect((host, port)) self.true_socket.sendall(data, *args, **kwargs) encoded_response = b'' - # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 - while select.select([self.true_socket], [], [], 0.5)[0]: - recv = self.true_socket.recv(self._buflen) - if recv: - encoded_response += recv - else: - break + + while not encoded_response: + # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 + while select.select([self.true_socket], [], [], 0.5)[0]: + recv = self.true_socket.recv(self._buflen) + if recv: + encoded_response += recv + else: + break + + if not encoded_response: + self.true_ssl_socket.connect((host, port)) + while select.select([self.true_ssl_socket], [], [], 0.5)[0]: + recv = self.true_ssl_socket.recv(self._buflen) + if recv: + encoded_response += recv + else: + break # dump the resulting dictionary to a JSON file if Mocket.get_truesocket_recording_dir(): diff --git a/mocket/utils.py b/mocket/utils.py index 3b238007..ff1b20c5 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -1,5 +1,6 @@ import io import os +import ssl class MocketSocketCore(io.BytesIO): @@ -10,3 +11,19 @@ def write(self, content): if Mocket.r_fd and Mocket.w_fd: os.write(Mocket.w_fd, content) + + +def wrap_ssl_socket(cls, sock, context, keyfile=None, certfile=None, + server_side=False, cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLS, ca_certs=None, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + ciphers=None): + return cls( + sock=sock, keyfile=keyfile, certfile=certfile, + server_side=server_side, cert_reqs=cert_reqs, + ssl_version=ssl_version, ca_certs=ca_certs, + do_handshake_on_connect=do_handshake_on_connect, + suppress_ragged_eofs=suppress_ragged_eofs, + ciphers=ciphers, _context=context + ) From becaf7327a8050da0bfcca6f8c7970cbcc79c684 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Fri, 7 Sep 2018 18:50:18 +0200 Subject: [PATCH 03/15] Merge from `master` (#78) * Adding yaml for Shippable CI. * Fix for pip 10 which misses .main() * Adding `recv_into` support (#72) * Adding socket.recv_into() plus its test (#71). * Forcing `pip` upgrade. * Fix for #65, thanks to the @jmcbailey patch at https://github.com/gabrielfalcao/HTTPretty/issues/242. * Improving the check, removing dependency of `requests` which is only acting as wrapper. * Increasing stability of `true_sendall` function. * Improving the `flake8` usage. * Minor changes. * Status badge from Shippable --- .gitignore | 2 ++ .travis.yml | 5 ++-- Makefile | 11 +++++-- README.rst | 4 +-- mocket/__init__.py | 2 +- mocket/mocket.py | 47 +++++++++++++++++------------- mocket/mockredis.py | 21 ++++++++----- mocket/plugins/httpretty/core.py | 2 +- mocket/plugins/pook_mock_engine.py | 1 - requirements.txt | 3 +- runtests.py | 4 +-- shippable.yaml | 18 ++++++++++++ test_requirements.txt | 2 +- tests/main/test_https.py | 1 + tests/main/test_mocket.py | 23 +++++++++++++++ 15 files changed, 104 insertions(+), 42 deletions(-) create mode 100644 shippable.yaml diff --git a/.gitignore b/.gitignore index 53fef38e..7af2b815 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ Documentation/ doc/build .buildinfo .coverage +shippable +.pytest_cache/ diff --git a/.travis.yml b/.travis.yml index 3c044210..b533197b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,13 +3,12 @@ services: language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" - # - "3.7" +# - "3.7-dev" # need to investigate the failure - "pypy" - - "pypy3" +# - "pypy3" install: - make develop script: diff --git a/Makefile b/Makefile index 44ee0a3c..d5ba3ed3 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ install-dev-requirements: pip install -U -q -e . install-test-requirements: + pip install -U pip pip install -U -q -e .[tests] test-python: @@ -13,13 +14,19 @@ test-python: lint-python: @echo "Linting Python files" - flake8 --exit-zero --ignore=E501 --exclude=.git,compat.py mocket + flake8 --ignore=E501,E731 --exclude=.git,compat.py mocket @echo "" develop: install-test-requirements install-dev-requirements + mkdir -p shippable/testresults + mkdir -p shippable/codecoverage test: install-test-requirements lint-python test-python +test-ci: install-test-requirements lint-python + python runtests.py --junitxml=shippable/testresults/nosetests.xml \ + --cov-report=xml:shippable/codecoverage/coverage.xml + safetest: export SKIP_TRUE_REDIS=1; export SKIP_TRUE_HTTP=1; make test @@ -33,5 +40,5 @@ clean: rm -rf *.egg-info find . -type d -name __pycache__ -exec rm -rf {} \; -.PHONY: publish clean +.PHONY: clean publish safetest test test-ci develop lint-python test-python install-test-requirements install-dev-requirements diff --git a/README.rst b/README.rst index 934ba2c9..81239b25 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ mocket /mɔˈkɛt/ =============== -.. image:: https://api.travis-ci.org/mindflayer/python-mocket.svg?branch=master - :target: http://travis-ci.org/mindflayer/python-mocket +.. image:: https://api.shippable.com/projects/5af070d0a55fb8070034316f/badge?branch=master + :target: https://app.shippable.com/github/mindflayer/python-mocket .. image:: https://coveralls.io/repos/github/mindflayer/python-mocket/badge.svg?branch=master :target: https://coveralls.io/github/mindflayer/python-mocket?branch=master diff --git a/mocket/__init__.py b/mocket/__init__.py index 60a9147d..42bd1856 100644 --- a/mocket/__init__.py +++ b/mocket/__init__.py @@ -7,4 +7,4 @@ __all__ = (mocketize, Mocket, MocketEntry, Mocketizer) -__version__ = '2.2.3' +__version__ = '2.4.0' diff --git a/mocket/mocket.py b/mocket/mocket.py index 0f920c97..e2104939 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -27,6 +27,13 @@ JSONDecodeError, ) +try: + from urllib3.contrib.pyopenssl import inject_into_urllib3, extract_from_urllib3 + pyopenssl_override = True +except ImportError: + pyopenssl_override = False + + __all__ = ( 'true_socket', 'true_create_connection', @@ -131,7 +138,7 @@ def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kw self.true_ssl_socket = wrap_ssl_socket(true_ssl_socket, true_socket(family, type, proto), true_ssl_context()) self.fd = MocketSocketCore() self._connected = False - self._buflen = 65536 + self._buflen = 4096 self._entry = None self.family = int(family) self.type = int(type) @@ -246,6 +253,9 @@ def sendall(self, data, entry=None, *args, **kwargs): def read(self, buffersize): return self.fd.read(buffersize) + def recv_into(self, buffer, buffersize, flags=None): + return buffer.write(self.fd.read(buffersize)) + def recv(self, buffersize, flags=None): if Mocket.r_fd and Mocket.w_fd: return os.read(Mocket.r_fd, buffersize) @@ -297,25 +307,16 @@ def true_sendall(self, data, *args, **kwargs): self.true_socket.connect((host, port)) self.true_socket.sendall(data, *args, **kwargs) - encoded_response = b'' - - while not encoded_response: - # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 - while select.select([self.true_socket], [], [], 0.5)[0]: - recv = self.true_socket.recv(self._buflen) - if recv: - encoded_response += recv - else: - break - - if not encoded_response: - self.true_ssl_socket.connect((host, port)) - while select.select([self.true_ssl_socket], [], [], 0.5)[0]: - recv = self.true_ssl_socket.recv(self._buflen) - if recv: - encoded_response += recv - else: - break + encoded_response = None + # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 + while True: + more_to_read = select.select([self.true_socket], [], [], 0.5)[0] + if not more_to_read and encoded_response is not None: + break + recv = self.true_socket.recv(self._buflen) + if not recv and encoded_response is not None: + break + encoded_response = encoded_response or b'' + recv # dump the resulting dictionary to a JSON file if Mocket.get_truesocket_recording_dir(): @@ -410,6 +411,9 @@ def enable(namespace=None, truesocket_recording_dir=None): '\x7f\x00\x00\x01', 'utf-8' ) + if pyopenssl_override: + # Take out the pyopenssl version - use the default implementation + extract_from_urllib3() @staticmethod def disable(): @@ -425,6 +429,9 @@ def disable(): ssl.SSLContext = ssl.__dict__['SSLContext'] = true_ssl_context socket.inet_pton = socket.__dict__['inet_pton'] = true_inet_pton Mocket.reset() + if pyopenssl_override: + # Put the pyopenssl version back in place + inject_into_urllib3() @classmethod def get_namespace(cls): diff --git a/mocket/mockredis.py b/mocket/mockredis.py index f984cb55..b6076a29 100644 --- a/mocket/mockredis.py +++ b/mocket/mockredis.py @@ -20,21 +20,26 @@ class Redisizer(byte_type): @staticmethod def tokens(iterable): iterable = [encode_to_bytes(x) for x in iterable] - return ['*{0}'.format(len(iterable)).encode('utf-8')] + list(chain(*zip(['${0}'.format(len(x)).encode('utf-8') for x in iterable], iterable))) + return [ + '*{0}'.format(len(iterable)).encode('utf-8') + ] + list( + chain(*zip(['${0}'.format(len(x)).encode('utf-8') for x in iterable], iterable)) + ) @staticmethod def redisize(data): + def get_conversion(t): + return { + dict: lambda x: b'\r\n'.join(Redisizer.tokens(list(chain(*tuple(x.items()))))), + int: lambda x: ':{0}'.format(x).encode('utf-8'), + text_type: lambda x: '${0}\r\n{1}'.format(len(x.encode('utf-8')), x).encode('utf-8'), + list: lambda x: b'\r\n'.join(Redisizer.tokens(x)), + }[t] if isinstance(data, Redisizer): return data if isinstance(data, byte_type): data = decode_from_bytes(data) - CONVERSION = { - dict: lambda x: b'\r\n'.join(Redisizer.tokens(list(chain(*tuple(x.items()))))), - int: lambda x: ':{0}'.format(x).encode('utf-8'), - text_type: lambda x: '${0}\r\n{1}'.format(len(x.encode('utf-8')), x).encode('utf-8'), - list: lambda x: b'\r\n'.join(Redisizer.tokens(x)), - } - return Redisizer(CONVERSION[type(data)](data) + b'\r\n') + return Redisizer(get_conversion(data.__class__)(data) + b'\r\n') @staticmethod def command(description, _type='+'): diff --git a/mocket/plugins/httpretty/core.py b/mocket/plugins/httpretty/core.py index 200b867e..5beedf08 100644 --- a/mocket/plugins/httpretty/core.py +++ b/mocket/plugins/httpretty/core.py @@ -1,3 +1,3 @@ -from mocket.compat import text_type, byte_type, decode_from_bytes +from mocket.compat import decode_from_bytes decode_utf8 = decode_from_bytes diff --git a/mocket/plugins/pook_mock_engine.py b/mocket/plugins/pook_mock_engine.py index b1059089..f9abecb6 100644 --- a/mocket/plugins/pook_mock_engine.py +++ b/mocket/plugins/pook_mock_engine.py @@ -67,4 +67,3 @@ def mocket_mock_fun(*args, **kwargs): # mocking pook.mock() self.pook_mock_fun = self.engine.mock self.engine.mock = mocket_mock_fun - diff --git a/requirements.txt b/requirements.txt index 76491b42..c36645a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ python-magic six decorator -hexdump \ No newline at end of file +hexdump +urllib3 \ No newline at end of file diff --git a/runtests.py b/runtests.py index 73eed1ad..aa7fcd3d 100644 --- a/runtests.py +++ b/runtests.py @@ -1,10 +1,10 @@ #!/usr/bin/env python import sys +import os def runtests(args=None): import pytest - import pip if not args: args = [] @@ -17,7 +17,7 @@ def runtests(args=None): if major == 3 and minor >= 5: python35 = True - pip.main(['install', 'aiohttp', 'async_timeout']) + os.system('pip install aiohttp async_timeout') if not any(a for a in args[1:] if not a.startswith('-')): args.append('tests/main') diff --git a/shippable.yaml b/shippable.yaml new file mode 100644 index 00000000..543bac25 --- /dev/null +++ b/shippable.yaml @@ -0,0 +1,18 @@ +language: python + +python: + - 2.7 + - 3.4 + - 3.5 + - 3.6 + #- 3.7 + - pypy + - pypy3 + +services: + - redis + +build: + ci: + - shippable_retry make develop + - make test-ci diff --git a/test_requirements.txt b/test_requirements.txt index 0c3aa97d..0daf05d6 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -5,4 +5,4 @@ mock requests redis gevent -sure \ No newline at end of file +sure diff --git a/tests/main/test_https.py b/tests/main/test_https.py index 197285e6..80871ea4 100644 --- a/tests/main/test_https.py +++ b/tests/main/test_https.py @@ -41,6 +41,7 @@ def test_json(response): recording_directory = tempfile.mkdtemp() +@pytest.mark.skipif('os.getenv("SKIP_TRUE_HTTP", False)') @mocketize(truesocket_recording_dir=recording_directory) def test_truesendall_with_recording_https(): url = 'https://httpbin.org/ip' diff --git a/tests/main/test_mocket.py b/tests/main/test_mocket.py index f253da63..b6f0a156 100644 --- a/tests/main/test_mocket.py +++ b/tests/main/test_mocket.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import socket from unittest import TestCase +import io import pytest @@ -94,6 +95,28 @@ def test_subsequent_recv_requests_have_correct_length(self): assert _so.recv(4096) == b'Short' _so.close() + def test_recv_into(self): + Mocket.register( + MocketEntry( + ('localhost', 80), + [ + b'Long payload', + b'Short' + ] + ) + ) + buffer = io.BytesIO() + with Mocketizer(): + _so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + _so.connect(('localhost', 80)) + _so.sendall(b'first\r\n') + assert _so.recv_into(buffer, 4096) == 12 + _so.sendall(b'second\r\n') + assert _so.recv_into(buffer, 4096) == 5 + _so.close() + buffer.seek(0) + assert buffer.read() == b'Long payloadShort' + class MocketizeTestCase(TestCase): def mocketize_setup(self): From 272f9c304535de0c0828fa8bb4e221d23038b703 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 19:59:42 +0200 Subject: [PATCH 04/15] Fix for real sendall on a secure socket. --- mocket/__init__.py | 1 - mocket/mocket.py | 223 +++++++++++++++++++++++++-------------------- mocket/utils.py | 33 +++++-- 3 files changed, 148 insertions(+), 109 deletions(-) diff --git a/mocket/__init__.py b/mocket/__init__.py index 7d656572..1661b3ca 100644 --- a/mocket/__init__.py +++ b/mocket/__init__.py @@ -8,4 +8,3 @@ __all__ = (mocketize, Mocket, MocketEntry, Mocketizer) __version__ = '2.6.0' - diff --git a/mocket/mocket.py b/mocket/mocket.py index 87832715..b20b80d9 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -13,9 +13,16 @@ import decorator import hexdump -from .compat import (FileNotFoundError, JSONDecodeError, basestring, byte_type, - decode_from_bytes, encode_to_bytes, text_type) -from .utils import MocketSocketCore +from .compat import ( + FileNotFoundError, + JSONDecodeError, + basestring, + byte_type, + decode_from_bytes, + encode_to_bytes, + text_type, +) +from .utils import MocketSocketCore, wrap_ssl_socket xxh32 = None try: @@ -29,33 +36,27 @@ try: from urllib3.contrib.pyopenssl import inject_into_urllib3, extract_from_urllib3 - pyopenssl_override = True -except ImportError: - pyopenssl_override = False - -try: - from urllib3.contrib.pyopenssl import inject_into_urllib3, extract_from_urllib3 pyopenssl_override = True except ImportError: pyopenssl_override = False __all__ = ( - 'true_socket', - 'true_create_connection', - 'true_gethostbyname', - 'true_gethostname', - 'true_getaddrinfo', - 'true_ssl_wrap_socket', - 'true_ssl_socket', - 'true_ssl_context', - 'true_inet_pton', - 'create_connection', - 'MocketSocket', - 'Mocket', - 'MocketEntry', - 'mocketize', + "true_socket", + "true_create_connection", + "true_gethostbyname", + "true_gethostname", + "true_getaddrinfo", + "true_ssl_wrap_socket", + "true_ssl_socket", + "true_ssl_context", + "true_inet_pton", + "create_connection", + "MocketSocket", + "Mocket", + "MocketEntry", + "mocketize", ) true_socket = socket.socket @@ -71,9 +72,11 @@ class SuperFakeSSLContext(object): """ For Python 3.6 """ + class FakeSetter(int): def __set__(self, *args): pass + options = FakeSetter() verify_mode = FakeSetter(ssl.CERT_OPTIONAL) @@ -89,14 +92,10 @@ def __init__(self, sock=None, server_hostname=None, _context=None, *args, **kwar self.sock.true_socket = true_ssl_socket( sock=self.sock.true_socket, server_hostname=server_hostname, - _context=true_ssl_context( - protocol=ssl.PROTOCOL_SSLv23, - ) + _context=true_ssl_context(protocol=ssl.PROTOCOL_SSLv23), ) else: # Python 2. - self.sock.true_socket = true_ssl_socket( - sock=self.sock.true_socket, - ) + self.sock.true_socket = true_ssl_socket(sock=self.sock.true_socket) elif isinstance(sock, int) and true_ssl_context: self.context = true_ssl_context(sock) @@ -110,14 +109,16 @@ def wrap_socket(sock=sock, *args, **kwargs): def wrap_bio(self, incoming, outcoming, *args, **kwargs): ssl_obj = MocketSocket() - ssl_obj._host = kwargs['server_hostname'] + ssl_obj._host = kwargs["server_hostname"] return ssl_obj def __getattr__(self, name): return getattr(self.sock, name) -def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): +def create_connection( + address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None +): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: s.settimeout(timeout) @@ -128,7 +129,7 @@ def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_ad def _hash_request(h, req): - return h(encode_to_bytes(''.join(sorted(req.split('\r\n'))))).hexdigest() + return h(encode_to_bytes("".join(sorted(req.split("\r\n"))))).hexdigest() class MocketSocket(object): @@ -143,10 +144,11 @@ class MocketSocket(object): _mode = None _bufsize = None - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kwargs): + def __init__( + self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kwargs + ): self.settimeout(socket._GLOBAL_DEFAULT_TIMEOUT) self.true_socket = true_socket(family, type, proto) - self.true_ssl_socket = wrap_ssl_socket(true_ssl_socket, true_socket(family, type, proto), true_ssl_context()) self.fd = MocketSocketCore() self._connected = False self._buflen = 4096 @@ -156,10 +158,14 @@ def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, **kw self.proto = int(proto) self._truesocket_recording_dir = None - sock = kwargs.get('sock') + sock = kwargs.get("sock") if sock is not None: self.__dict__ = dict(sock.__dict__) + self.true_socket = wrap_ssl_socket( + true_ssl_socket, self.true_socket, true_ssl_context() + ) + def __unicode__(self): return str(self) @@ -204,23 +210,16 @@ def getpeercert(self, *args, **kwargs): now = datetime.now() shift = now + timedelta(days=30 * 12) return { - 'notAfter': shift.strftime('%b %d %H:%M:%S GMT'), - 'subjectAltName': ( - ('DNS', '*%s' % self._host), - ('DNS', self._host), - ('DNS', '*'), + "notAfter": shift.strftime("%b %d %H:%M:%S GMT"), + "subjectAltName": ( + ("DNS", "*%s" % self._host), + ("DNS", self._host), + ("DNS", "*"), ), - 'subject': ( - ( - ('organizationName', '*.%s' % self._host), - ), - ( - ('organizationalUnitName', - 'Domain Control Validated'), - ), - ( - ('commonName', '*.%s' % self._host), - ), + "subject": ( + (("organizationName", "*.%s" % self._host),), + (("organizationalUnitName", "Domain Control Validated"),), + (("commonName", "*.%s" % self._host),), ), } @@ -238,7 +237,7 @@ def connect(self, address): self._address = self._host, self._port = address Mocket._address = address - def makefile(self, mode='r', bufsize=-1): + def makefile(self, mode="r", bufsize=-1): self._mode = mode self._bufsize = bufsize return self.fd @@ -284,8 +283,7 @@ def true_sendall(self, data, *args, **kwargs): if Mocket.get_truesocket_recording_dir(): path = os.path.join( - Mocket.get_truesocket_recording_dir(), - Mocket.get_namespace() + '.json', + Mocket.get_truesocket_recording_dir(), Mocket.get_namespace() + ".json" ) # check if there's already a recorded session dumped to a JSON file try: @@ -315,16 +313,22 @@ def true_sendall(self, data, *args, **kwargs): # try to get the response from the dictionary try: try: - encoded_response = hexdump.dehex(response_dict['response']) + encoded_response = hexdump.dehex(response_dict["response"]) except TypeError: # pragma: no cover # Python 2 - encoded_response = hexdump.restore(encode_to_bytes(response_dict['response'])) + encoded_response = hexdump.restore( + encode_to_bytes(response_dict["response"]) + ) # if not available, call the real sendall except KeyError: host, port = Mocket._address host = true_gethostbyname(host) - self.true_socket.connect((host, port)) + try: + self.true_socket.connect((host, port)) + except OSError: + # already connected + pass self.true_socket.sendall(data, *args, **kwargs) encoded_response = None # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 @@ -335,17 +339,21 @@ def true_sendall(self, data, *args, **kwargs): recv = self.true_socket.recv(self._buflen) if not recv and encoded_response is not None: break - encoded_response = encoded_response or b'' + recv + encoded_response = encoded_response or b"" + recv # dump the resulting dictionary to a JSON file if Mocket.get_truesocket_recording_dir(): # update the dictionary with request and response lines - response_dict['request'] = req - response_dict['response'] = hexdump.dump(encoded_response) - - with io.open(path, mode='w') as f: - f.write(decode_from_bytes(json.dumps(responses, indent=4, sort_keys=True))) + response_dict["request"] = req + response_dict["response"] = hexdump.dump(encoded_response) + + with io.open(path, mode="w") as f: + f.write( + decode_from_bytes( + json.dumps(responses, indent=4, sort_keys=True) + ) + ) # response back to .sendall() which writes it to the mocket socket and flush the BytesIO return encoded_response @@ -356,7 +364,7 @@ def send(self, data, *args, **kwargs): # pragma: no cover self.sendall(data, entry=entry, *args, **kwargs) else: req = Mocket.last_request() - if hasattr(req, 'add_data'): + if hasattr(req, "add_data"): req.add_data(decode_from_bytes(data)) self._entry = entry return len(data) @@ -415,20 +423,26 @@ def enable(namespace=None, truesocket_recording_dir=None): # JSON dumps will be saved here assert os.path.isdir(truesocket_recording_dir) - socket.socket = socket.__dict__['socket'] = MocketSocket - socket._socketobject = socket.__dict__['_socketobject'] = MocketSocket - socket.SocketType = socket.__dict__['SocketType'] = MocketSocket - socket.create_connection = socket.__dict__['create_connection'] = create_connection - socket.gethostname = socket.__dict__['gethostname'] = lambda: 'localhost' - socket.gethostbyname = socket.__dict__['gethostbyname'] = lambda host: '127.0.0.1' - socket.getaddrinfo = socket.__dict__['getaddrinfo'] = \ - lambda host, port, family=None, socktype=None, proto=None, flags=None: [(2, 1, 6, '', (host, port))] - ssl.wrap_socket = ssl.__dict__['wrap_socket'] = FakeSSLContext.wrap_socket - ssl.SSLSocket = ssl.__dict__['SSLSocket'] = MocketSocket - ssl.SSLContext = ssl.__dict__['SSLContext'] = FakeSSLContext - socket.inet_pton = socket.__dict__['inet_pton'] = lambda family, ip: byte_type( - '\x7f\x00\x00\x01', - 'utf-8' + socket.socket = socket.__dict__["socket"] = MocketSocket + socket._socketobject = socket.__dict__["_socketobject"] = MocketSocket + socket.SocketType = socket.__dict__["SocketType"] = MocketSocket + socket.create_connection = socket.__dict__[ + "create_connection" + ] = create_connection + socket.gethostname = socket.__dict__["gethostname"] = lambda: "localhost" + socket.gethostbyname = socket.__dict__[ + "gethostbyname" + ] = lambda host: "127.0.0.1" + socket.getaddrinfo = socket.__dict__[ + "getaddrinfo" + ] = lambda host, port, family=None, socktype=None, proto=None, flags=None: [ + (2, 1, 6, "", (host, port)) + ] + ssl.wrap_socket = ssl.__dict__["wrap_socket"] = FakeSSLContext.wrap_socket + ssl.SSLSocket = ssl.__dict__["SSLSocket"] = MocketSocket + ssl.SSLContext = ssl.__dict__["SSLContext"] = FakeSSLContext + socket.inet_pton = socket.__dict__["inet_pton"] = lambda family, ip: byte_type( + "\x7f\x00\x00\x01", "utf-8" ) if pyopenssl_override: # Take out the pyopenssl version - use the default implementation @@ -436,17 +450,19 @@ def enable(namespace=None, truesocket_recording_dir=None): @staticmethod def disable(): - socket.socket = socket.__dict__['socket'] = true_socket - socket._socketobject = socket.__dict__['_socketobject'] = true_socket - socket.SocketType = socket.__dict__['SocketType'] = true_socket - socket.create_connection = socket.__dict__['create_connection'] = true_create_connection - socket.gethostname = socket.__dict__['gethostname'] = true_gethostname - socket.gethostbyname = socket.__dict__['gethostbyname'] = true_gethostbyname - socket.getaddrinfo = socket.__dict__['getaddrinfo'] = true_getaddrinfo - ssl.wrap_socket = ssl.__dict__['wrap_socket'] = true_ssl_wrap_socket - ssl.SSLSocket = ssl.__dict__['SSLSocket'] = true_ssl_socket - ssl.SSLContext = ssl.__dict__['SSLContext'] = true_ssl_context - socket.inet_pton = socket.__dict__['inet_pton'] = true_inet_pton + socket.socket = socket.__dict__["socket"] = true_socket + socket._socketobject = socket.__dict__["_socketobject"] = true_socket + socket.SocketType = socket.__dict__["SocketType"] = true_socket + socket.create_connection = socket.__dict__[ + "create_connection" + ] = true_create_connection + socket.gethostname = socket.__dict__["gethostname"] = true_gethostname + socket.gethostbyname = socket.__dict__["gethostbyname"] = true_gethostbyname + socket.getaddrinfo = socket.__dict__["getaddrinfo"] = true_getaddrinfo + ssl.wrap_socket = ssl.__dict__["wrap_socket"] = true_ssl_wrap_socket + ssl.SSLSocket = ssl.__dict__["SSLSocket"] = true_ssl_socket + ssl.SSLContext = ssl.__dict__["SSLContext"] = true_ssl_context + socket.inet_pton = socket.__dict__["inet_pton"] = true_inet_pton Mocket.reset() if pyopenssl_override: # Put the pyopenssl version back in place @@ -462,7 +478,6 @@ def get_truesocket_recording_dir(cls): class MocketEntry(object): - class Response(byte_type): @property def data(self): @@ -475,19 +490,21 @@ def __init__(self, location, responses): self.location = location self.response_index = 0 - if not isinstance(responses, collections.Iterable) or isinstance(responses, basestring): + if not isinstance(responses, collections.Iterable) or isinstance( + responses, basestring + ): responses = [responses] lresponses = [] for r in responses: - if not getattr(r, 'data', False): + if not getattr(r, "data", False): if isinstance(r, text_type): r = encode_to_bytes(r) r = self.response_cls(r) lresponses.append(r) else: if not responses: - lresponses = [self.response_cls(encode_to_bytes(''))] + lresponses = [self.response_cls(encode_to_bytes(""))] self.responses = lresponses def can_handle(self, data): @@ -511,13 +528,16 @@ def __init__(self, instance=None, namespace=None, truesocket_recording_dir=None) self.namespace = namespace or text_type(id(self)) def __enter__(self): - Mocket.enable(namespace=self.namespace, truesocket_recording_dir=self.truesocket_recording_dir) + Mocket.enable( + namespace=self.namespace, + truesocket_recording_dir=self.truesocket_recording_dir, + ) if self.instance: - self.check_and_call('mocketize_setup') + self.check_and_call("mocketize_setup") def __exit__(self, type, value, tb): if self.instance: - self.check_and_call('mocketize_teardown') + self.check_and_call("mocketize_teardown") Mocket.disable() def check_and_call(self, method): @@ -529,10 +549,17 @@ def check_and_call(self, method): def wrap(test=None, truesocket_recording_dir=None): def wrapper(t, *args, **kw): instance = args[0] if args else None - namespace = '.'.join((instance.__class__.__module__, instance.__class__.__name__, t.__name__)) - with Mocketizer(instance, namespace=namespace, truesocket_recording_dir=truesocket_recording_dir): + namespace = ".".join( + (instance.__class__.__module__, instance.__class__.__name__, t.__name__) + ) + with Mocketizer( + instance, + namespace=namespace, + truesocket_recording_dir=truesocket_recording_dir, + ): t(*args, **kw) return wrapper + return decorator.decorator(wrapper, test) diff --git a/mocket/utils.py b/mocket/utils.py index ff1b20c5..7e67f65d 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -13,17 +13,30 @@ def write(self, content): os.write(Mocket.w_fd, content) -def wrap_ssl_socket(cls, sock, context, keyfile=None, certfile=None, - server_side=False, cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - ciphers=None): +def wrap_ssl_socket( + cls, + sock, + context, + keyfile=None, + certfile=None, + server_side=False, + cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLS, + ca_certs=None, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + ciphers=None, +): return cls( - sock=sock, keyfile=keyfile, certfile=certfile, - server_side=server_side, cert_reqs=cert_reqs, - ssl_version=ssl_version, ca_certs=ca_certs, + sock=sock, + keyfile=keyfile, + certfile=certfile, + server_side=server_side, + cert_reqs=cert_reqs, + ssl_version=ssl_version, + ca_certs=ca_certs, do_handshake_on_connect=do_handshake_on_connect, suppress_ragged_eofs=suppress_ragged_eofs, - ciphers=ciphers, _context=context + ciphers=ciphers, + _context=context, ) From c878a8a5ed99c32b563805ccf033de004c2f97dc Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 20:07:01 +0200 Subject: [PATCH 05/15] Useless import. --- tests/main/test_mocket.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/main/test_mocket.py b/tests/main/test_mocket.py index 3c53bd58..0db176cf 100644 --- a/tests/main/test_mocket.py +++ b/tests/main/test_mocket.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -import io import socket from unittest import TestCase import io From 904d78623f130b54acbceeb0e93f942979ee6dbb Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 20:08:08 +0200 Subject: [PATCH 06/15] Useless import. --- runtests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/runtests.py b/runtests.py index 4465e348..4d33d7d3 100644 --- a/runtests.py +++ b/runtests.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import os import sys -import os def runtests(args=None): From 46a8ceb073b6991b3eb3908b8ef335599f6f1eb2 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 21:17:36 +0200 Subject: [PATCH 07/15] Fix. --- mocket/mocket.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index b20b80d9..359faafc 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -151,7 +151,7 @@ def __init__( self.true_socket = true_socket(family, type, proto) self.fd = MocketSocketCore() self._connected = False - self._buflen = 4096 + self._buflen = 65536 self._entry = None self.family = int(family) self.type = int(type) @@ -330,16 +330,16 @@ def true_sendall(self, data, *args, **kwargs): # already connected pass self.true_socket.sendall(data, *args, **kwargs) - encoded_response = None + encoded_response = b'' # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 while True: - more_to_read = select.select([self.true_socket], [], [], 0.5)[0] - if not more_to_read and encoded_response is not None: + if not select.select([self.true_socket], [], [], 0.1)[0] and encoded_response: break recv = self.true_socket.recv(self._buflen) - if not recv and encoded_response is not None: + + if not recv and encoded_response: break - encoded_response = encoded_response or b"" + recv + encoded_response += recv # dump the resulting dictionary to a JSON file if Mocket.get_truesocket_recording_dir(): From c2888853ed893b166cca87dda648026094d5fe69 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 21:21:14 +0200 Subject: [PATCH 08/15] Upgrading pytest. --- test_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_requirements.txt b/test_requirements.txt index bc8525c4..e1682497 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,5 +1,5 @@ pep8 -pytest +pytest==4.4.0 pytest-cov mock requests From f5cfb315d90ec7e13ae8956d463f40ddc3810cbb Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 21:46:35 +0200 Subject: [PATCH 09/15] Last versions of redis are not compatible with mocket. --- test_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_requirements.txt b/test_requirements.txt index e1682497..555ea519 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -3,7 +3,7 @@ pytest==4.4.0 pytest-cov mock requests -redis +redis<3.2.0 gevent sure pook From 863eb7b1a4943631adbbf2bf0d1adb8357475a67 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:13:56 +0200 Subject: [PATCH 10/15] Fix for SSL protocol. --- mocket/mocket.py | 4 ++-- mocket/utils.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index 359faafc..bb41cf96 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -22,7 +22,7 @@ encode_to_bytes, text_type, ) -from .utils import MocketSocketCore, wrap_ssl_socket +from .utils import MocketSocketCore, wrap_ssl_socket, SSL_PROTOCOL xxh32 = None try: @@ -92,7 +92,7 @@ def __init__(self, sock=None, server_hostname=None, _context=None, *args, **kwar self.sock.true_socket = true_ssl_socket( sock=self.sock.true_socket, server_hostname=server_hostname, - _context=true_ssl_context(protocol=ssl.PROTOCOL_SSLv23), + _context=true_ssl_context(protocol=SSL_PROTOCOL), ) else: # Python 2. self.sock.true_socket = true_ssl_socket(sock=self.sock.true_socket) diff --git a/mocket/utils.py b/mocket/utils.py index 7e67f65d..b20001d0 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -3,6 +3,9 @@ import ssl +SSL_PROTOCOL = ssl.PROTOCOL_SSLv23 + + class MocketSocketCore(io.BytesIO): def write(self, content): super(MocketSocketCore, self).write(content) @@ -21,7 +24,7 @@ def wrap_ssl_socket( certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_TLS, + ssl_version=SSL_PROTOCOL, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, From 6646d45214d0c383a68cc2379bd4aff9aa905bff Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:16:57 +0200 Subject: [PATCH 11/15] Fix for SSL protocol. --- mocket/mocket.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index bb41cf96..9504a243 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -163,7 +163,9 @@ def __init__( self.__dict__ = dict(sock.__dict__) self.true_socket = wrap_ssl_socket( - true_ssl_socket, self.true_socket, true_ssl_context() + true_ssl_socket, + self.true_socket, + true_ssl_context(protocol=SSL_PROTOCOL), ) def __unicode__(self): @@ -330,10 +332,13 @@ def true_sendall(self, data, *args, **kwargs): # already connected pass self.true_socket.sendall(data, *args, **kwargs) - encoded_response = b'' + encoded_response = b"" # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 while True: - if not select.select([self.true_socket], [], [], 0.1)[0] and encoded_response: + if ( + not select.select([self.true_socket], [], [], 0.1)[0] + and encoded_response + ): break recv = self.true_socket.recv(self._buflen) From a58ebd03ee84375add6c40cb3deb66b8989b7621 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:23:16 +0200 Subject: [PATCH 12/15] PEP8. --- mocket/mocket.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index 9504a243..13af8265 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -335,10 +335,9 @@ def true_sendall(self, data, *args, **kwargs): encoded_response = b"" # https://github.com/kennethreitz/requests/blob/master/tests/testserver/server.py#L13 while True: - if ( - not select.select([self.true_socket], [], [], 0.1)[0] - and encoded_response - ): + if not select.select( + [self.true_socket], [], [], 0.1 + )[0] and encoded_response: break recv = self.true_socket.recv(self._buflen) From 90106e47967b7925fc971f854ec28192f2706263 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:39:45 +0200 Subject: [PATCH 13/15] Fix for PY2. --- mocket/mocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocket/mocket.py b/mocket/mocket.py index 13af8265..95329cad 100644 --- a/mocket/mocket.py +++ b/mocket/mocket.py @@ -328,7 +328,7 @@ def true_sendall(self, data, *args, **kwargs): try: self.true_socket.connect((host, port)) - except OSError: + except (OSError, socket.error): # already connected pass self.true_socket.sendall(data, *args, **kwargs) From 6a04d1b3b2131de9c5d76b7349499f6cea28c3fb Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:53:44 +0200 Subject: [PATCH 14/15] Bumping version. --- mocket/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocket/__init__.py b/mocket/__init__.py index 1661b3ca..79405035 100644 --- a/mocket/__init__.py +++ b/mocket/__init__.py @@ -7,4 +7,4 @@ __all__ = (mocketize, Mocket, MocketEntry, Mocketizer) -__version__ = '2.6.0' +__version__ = '2.7.0' From c6c3dd6d50a8c134cfbb36acc44b49793309091c Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 2 Apr 2019 22:57:55 +0200 Subject: [PATCH 15/15] Revert. [ci skip] --- tests/main/test_mocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/main/test_mocket.py b/tests/main/test_mocket.py index 0db176cf..a15a324d 100644 --- a/tests/main/test_mocket.py +++ b/tests/main/test_mocket.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals +import io import socket from unittest import TestCase -import io import pytest