pax_global_header00006660000000000000000000000064130257504440014516gustar00rootroot0000000000000052 comment=67dffa231e0fb24a47b931f1054b8b9f39061b8d httmock-1.2.6/000077500000000000000000000000001302575044400131755ustar00rootroot00000000000000httmock-1.2.6/.coveragerc000066400000000000000000000001641302575044400153170ustar00rootroot00000000000000[run] omit = tests.py source = httmock [report] exclude_lines = pragma: no cover raise NotImplementedError httmock-1.2.6/.gitignore000066400000000000000000000004571302575044400151730ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject httmock-1.2.6/.travis.yml000066400000000000000000000002161302575044400153050ustar00rootroot00000000000000language: python python: - 2.6 - 2.7 - pypy - 3.3 - 3.4 - 3.5 install: - "pip install requests" script: nosetests httmock-1.2.6/LICENSE000066400000000000000000000011051302575044400141770ustar00rootroot00000000000000Copyright 2013 Patryk Zawadzki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. httmock-1.2.6/MANIFEST.in000066400000000000000000000000321302575044400147260ustar00rootroot00000000000000include README.md LICENSE httmock-1.2.6/README.md000066400000000000000000000033231302575044400144550ustar00rootroot00000000000000httmock ======= A mocking library for `requests` for Python 2.6, 2.7, 3.2, 3.3 and 3.4. Installation ------------ pip install httmock Or, if you are a Gentoo user: emerge dev-python/httmock Usage ----- You can use it to mock third-party APIs and test libraries that use `requests` internally, conditionally using mocked replies with the `urlmatch` decorator: ```python from httmock import urlmatch, HTTMock import requests @urlmatch(netloc=r'(.*\.)?google\.com$') def google_mock(url, request): return 'Feeling lucky, punk?' with HTTMock(google_mock): r = requests.get('http://google.com/') print r.content # 'Feeling lucky, punk?' ``` The `all_requests` decorator doesn't conditionally block real requests. If you return a dictionary, it will map to the `requests.Response` object returned: ```python from httmock import all_requests, HTTMock import requests @all_requests def response_content(url, request): return {'status_code': 200, 'content': 'Oh hai'} with HTTMock(response_content): r = requests.get('https://foo_bar') print r.status_code print r.content ``` If you pass in `Set-Cookie` headers, `requests.Response.cookies` will contain the values. You can also use `response` method directly instead of returning a dict: ```python from httmock import all_requests, response, HTTMock import requests @all_requests def response_content(url, request): headers = {'content-type': 'application/json', 'Set-Cookie': 'foo=bar;'} content = {'message': 'API rate limit exceeded'} return response(403, content, headers, None, 5, request) with HTTMock(response_content): r = requests.get('https://api.github.com/users/whatever') print r.json().get('message') print r.cookies['foo'] ``` httmock-1.2.6/httmock.py000066400000000000000000000166341302575044400152320ustar00rootroot00000000000000from functools import wraps import datetime from requests import cookies import json import re import requests from requests import structures import sys try: import urlparse except ImportError: import urllib.parse as urlparse if sys.version_info >= (3, 0, 0): from io import BytesIO else: try: from cStringIO import StringIO as BytesIO except ImportError: from StringIO import StringIO as BytesIO binary_type = bytes if sys.version_info >= (3, 0, 0): text_type = str else: text_type = unicode # noqa class Headers(object): def __init__(self, res): self.headers = res.headers def get_all(self, name, failobj=None): return self.getheaders(name) def getheaders(self, name): return [self.headers.get(name)] def response(status_code=200, content='', headers=None, reason=None, elapsed=0, request=None, stream=False): res = requests.Response() res.status_code = status_code if isinstance(content, (dict, list)): content = json.dumps(content).encode('utf-8') if isinstance(content, text_type): content = content.encode('utf-8') res._content = content res._content_consumed = content res.headers = structures.CaseInsensitiveDict(headers or {}) res.reason = reason res.elapsed = datetime.timedelta(elapsed) res.request = request if hasattr(request, 'url'): res.url = request.url if isinstance(request.url, bytes): res.url = request.url.decode('utf-8') if 'set-cookie' in res.headers: res.cookies.extract_cookies(cookies.MockResponse(Headers(res)), cookies.MockRequest(request)) if stream: res.raw = BytesIO(content) else: res.raw = BytesIO(b'') # normally this closes the underlying connection, # but we have nothing to free. res.close = lambda *args, **kwargs: None return res def all_requests(func): @wraps(func) def inner(*args, **kwargs): return func(*args, **kwargs) return inner def urlmatch(scheme=None, netloc=None, path=None, method=None, query=None): def decorator(func): @wraps(func) def inner(self_or_url, url_or_request, *args, **kwargs): if isinstance(self_or_url, urlparse.SplitResult): url = self_or_url request = url_or_request else: url = url_or_request request = args[0] if scheme is not None and scheme != url.scheme: return if netloc is not None and not re.match(netloc, url.netloc): return if path is not None and not re.match(path, url.path): return if query is not None and not re.match(query, url.query): return if method is not None and method.upper() != request.method: return return func(self_or_url, url_or_request, *args, **kwargs) return inner return decorator def handler_init_call(handler): setattr(handler, 'call', { 'count': 0, 'called': False }) def handler_clean_call(handler): if hasattr(handler, 'call'): handler.call.update({ 'count': 0, 'called': False }) def handler_called(handler, *args, **kwargs): try: return handler(*args, **kwargs) finally: handler.call['count'] += 1 handler.call['called'] = True def remember_called(func): handler_init_call(func) @wraps(func) def inner(*args, **kwargs): return handler_called(func, *args, **kwargs) return inner def first_of(handlers, *args, **kwargs): for handler in handlers: res = handler(*args, **kwargs) if res is not None: return res class HTTMock(object): """ Acts as a context manager to allow mocking """ STATUS_CODE = 200 def __init__(self, *handlers): self.handlers = handlers def __enter__(self): self._real_session_send = requests.Session.send self._real_session_prepare_request = requests.Session.prepare_request for handler in self.handlers: handler_clean_call(handler) def _fake_send(session, request, **kwargs): response = self.intercept(request, **kwargs) if isinstance(response, requests.Response): # this is pasted from requests to handle redirects properly: kwargs.setdefault('stream', session.stream) kwargs.setdefault('verify', session.verify) kwargs.setdefault('cert', session.cert) kwargs.setdefault('proxies', session.proxies) allow_redirects = kwargs.pop('allow_redirects', True) stream = kwargs.get('stream') timeout = kwargs.get('timeout') verify = kwargs.get('verify') cert = kwargs.get('cert') proxies = kwargs.get('proxies') gen = session.resolve_redirects( response, request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies) history = [resp for resp in gen] if allow_redirects else [] if history: history.insert(0, response) response = history.pop() response.history = tuple(history) session.cookies = response.cookies return response return self._real_session_send(session, request, **kwargs) def _fake_prepare_request(session, request): """ Fake this method so the `PreparedRequest` objects contains an attribute `original` of the original request. """ prep = self._real_session_prepare_request(session, request) prep.original = request return prep requests.Session.send = _fake_send requests.Session.prepare_request = _fake_prepare_request return self def __exit__(self, exc_type, exc_val, exc_tb): requests.Session.send = self._real_session_send requests.Session.prepare_request = self._real_session_prepare_request def intercept(self, request, **kwargs): url = urlparse.urlsplit(request.url) res = first_of(self.handlers, url, request) if isinstance(res, requests.Response): return res elif isinstance(res, dict): return response(res.get('status_code'), res.get('content'), res.get('headers'), res.get('reason'), res.get('elapsed', 0), request, stream=kwargs.get('stream', False)) elif isinstance(res, (text_type, binary_type)): return response(content=res, stream=kwargs.get('stream', False)) elif res is None: return None else: raise TypeError( "Dont know how to handle response of type {0}".format(type(res))) def with_httmock(*handlers): mock = HTTMock(*handlers) def decorator(func): @wraps(func) def inner(*args, **kwargs): with mock: return func(*args, **kwargs) return inner return decorator httmock-1.2.6/setup.py000077500000000000000000000015321302575044400147130ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup import os LICENSE = open( os.path.join(os.path.dirname(__file__), 'LICENSE')).read().strip() DESCRIPTION = open( os.path.join(os.path.dirname(__file__), 'README.md')).read().strip() setup( name='httmock', version='1.2.6', description='A mocking library for requests.', author='Patryk Zawadzki', author_email='patrys@room-303.com', url='https://github.com/patrys/httmock', py_modules=['httmock'], keywords=['requests', 'testing', 'mock'], classifiers=[ 'Programming Language :: Python', 'Intended Audience :: Developers', 'Topic :: Software Development :: Testing', 'Operating System :: OS Independent'], install_requires=['requests >= 1.0.0'], license=LICENSE, long_description=DESCRIPTION, test_suite='tests') httmock-1.2.6/tests.py000066400000000000000000000266401302575044400147210ustar00rootroot00000000000000import requests import unittest from httmock import (all_requests, response, urlmatch, with_httmock, HTTMock, remember_called, text_type, binary_type) @urlmatch(scheme='swallow') def unmatched_scheme(url, request): raise AssertionError('This is outrageous') @urlmatch(path=r'^never$') def unmatched_path(url, request): raise AssertionError('This is outrageous') @urlmatch(method='post') def unmatched_method(url, request): raise AssertionError('This is outrageous') @urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') def google_mock(url, request): return 'Hello from Google' @urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') @remember_called def google_mock_count(url, request): return 'Hello from Google' @urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') def facebook_mock(url, request): return 'Hello from Facebook' @urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') @remember_called def facebook_mock_count(url, request): return 'Hello from Facebook' def any_mock(url, request): return 'Hello from %s' % (url.netloc,) def dict_any_mock(url, request): return { 'content': 'Hello from %s' % (url.netloc,), 'status_code': 200 } def example_400_response(url, response): r = requests.Response() r.status_code = 400 r._content = b'Bad request.' return r class MockTest(unittest.TestCase): def test_return_type(self): with HTTMock(any_mock): r = requests.get('http://domain.com/') self.assertTrue(isinstance(r, requests.Response)) self.assertTrue(isinstance(r.content, binary_type)) self.assertTrue(isinstance(r.text, text_type)) def test_scheme_fallback(self): with HTTMock(unmatched_scheme, any_mock): r = requests.get('http://example.com/') self.assertEqual(r.content, b'Hello from example.com') def test_path_fallback(self): with HTTMock(unmatched_path, any_mock): r = requests.get('http://example.com/') self.assertEqual(r.content, b'Hello from example.com') def test_method_fallback(self): with HTTMock(unmatched_method, any_mock): r = requests.get('http://example.com/') self.assertEqual(r.content, b'Hello from example.com') def test_netloc_fallback(self): with HTTMock(google_mock, facebook_mock): r = requests.get('http://google.com/') self.assertEqual(r.content, b'Hello from Google') with HTTMock(google_mock, facebook_mock): r = requests.get('http://facebook.com/') self.assertEqual(r.content, b'Hello from Facebook') def test_400_response(self): with HTTMock(example_400_response): r = requests.get('http://example.com/') self.assertEqual(r.status_code, 400) self.assertEqual(r.content, b'Bad request.') def test_real_request_fallback(self): with HTTMock(any_mock): with HTTMock(google_mock, facebook_mock): r = requests.get('http://example.com/') self.assertEqual(r.status_code, 200) self.assertEqual(r.content, b'Hello from example.com') def test_invalid_intercept_response_raises_value_error(self): @all_requests def response_content(url, request): return -1 with HTTMock(response_content): self.assertRaises(TypeError, requests.get, 'http://example.com/') class DecoratorTest(unittest.TestCase): @with_httmock(any_mock) def test_decorator(self): r = requests.get('http://example.com/') self.assertEqual(r.content, b'Hello from example.com') @with_httmock(any_mock) def test_iter_lines(self): r = requests.get('http://example.com/') self.assertEqual(list(r.iter_lines()), [b'Hello from example.com']) class AllRequestsDecoratorTest(unittest.TestCase): def test_all_requests_response(self): @all_requests def response_content(url, request): return {'status_code': 200, 'content': 'Oh hai'} with HTTMock(response_content): r = requests.get('https://example.com/') self.assertEqual(r.status_code, 200) self.assertEqual(r.content, b'Oh hai') def test_all_str_response(self): @all_requests def response_content(url, request): return 'Hello' with HTTMock(response_content): r = requests.get('https://example.com/') self.assertEqual(r.content, b'Hello') class AllRequestsMethodDecoratorTest(unittest.TestCase): @all_requests def response_content(self, url, request): return {'status_code': 200, 'content': 'Oh hai'} def test_all_requests_response(self): with HTTMock(self.response_content): r = requests.get('https://example.com/') self.assertEqual(r.status_code, 200) self.assertEqual(r.content, b'Oh hai') @all_requests def string_response_content(self, url, request): return 'Hello' def test_all_str_response(self): with HTTMock(self.string_response_content): r = requests.get('https://example.com/') self.assertEqual(r.content, b'Hello') class UrlMatchMethodDecoratorTest(unittest.TestCase): @urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') def google_mock(self, url, request): return 'Hello from Google' @urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') def facebook_mock(self, url, request): return 'Hello from Facebook' @urlmatch(query=r'.*page=test') def query_page_mock(self, url, request): return 'Hello from test page' def test_netloc_fallback(self): with HTTMock(self.google_mock, facebook_mock): r = requests.get('http://google.com/') self.assertEqual(r.content, b'Hello from Google') with HTTMock(self.google_mock, facebook_mock): r = requests.get('http://facebook.com/') self.assertEqual(r.content, b'Hello from Facebook') def test_query(self): with HTTMock(self.query_page_mock, self.google_mock): r = requests.get('http://google.com/?page=test') r2 = requests.get('http://google.com/') self.assertEqual(r.content, b'Hello from test page') self.assertEqual(r2.content, b'Hello from Google') class ResponseTest(unittest.TestCase): content = {'name': 'foo', 'ipv4addr': '127.0.0.1'} content_list = list(content.keys()) def test_response_auto_json(self): r = response(0, self.content) self.assertTrue(isinstance(r.content, binary_type)) self.assertTrue(isinstance(r.text, text_type)) self.assertEqual(r.json(), self.content) r = response(0, self.content_list) self.assertEqual(r.json(), self.content_list) def test_response_status_code(self): r = response(200) self.assertEqual(r.status_code, 200) def test_response_headers(self): r = response(200, None, {'Content-Type': 'application/json'}) self.assertEqual(r.headers['content-type'], 'application/json') def test_response_cookies(self): @all_requests def response_content(url, request): return response(200, 'Foo', {'Set-Cookie': 'foo=bar;'}, request=request) with HTTMock(response_content): r = requests.get('https://example.com/') self.assertEqual(len(r.cookies), 1) self.assertTrue('foo' in r.cookies) self.assertEqual(r.cookies['foo'], 'bar') def test_response_session_cookies(self): @all_requests def response_content(url, request): return response(200, 'Foo', {'Set-Cookie': 'foo=bar;'}, request=request) session = requests.Session() with HTTMock(response_content): r = session.get('https://foo_bar') self.assertEqual(len(r.cookies), 1) self.assertTrue('foo' in r.cookies) self.assertEqual(r.cookies['foo'], 'bar') self.assertEqual(len(session.cookies), 1) self.assertTrue('foo' in session.cookies) self.assertEqual(session.cookies['foo'], 'bar') def test_python_version_encoding_differences(self): # Previous behavior would result in this test failing in Python3 due # to how requests checks for utf-8 JSON content in requests.utils with: # # TypeError: Can't convert 'bytes' object to str implicitly @all_requests def get_mock(url, request): return {'content': self.content, 'headers': {'content-type': 'application/json'}, 'status_code': 200, 'elapsed': 5} with HTTMock(get_mock): response = requests.get('http://example.com/') self.assertEqual(self.content, response.json()) def test_mock_redirect(self): @urlmatch(netloc='example.com') def get_mock(url, request): return {'status_code': 302, 'headers': {'Location': 'http://google.com/'}} with HTTMock(get_mock, google_mock): response = requests.get('http://example.com/') self.assertEqual(len(response.history), 1) self.assertEqual(response.content, b'Hello from Google') class StreamTest(unittest.TestCase): @with_httmock(any_mock) def test_stream_request(self): r = requests.get('http://domain.com/', stream=True) self.assertEqual(r.raw.read(), b'Hello from domain.com') @with_httmock(dict_any_mock) def test_stream_request_with_dict_mock(self): r = requests.get('http://domain.com/', stream=True) self.assertEqual(r.raw.read(), b'Hello from domain.com') @with_httmock(any_mock) def test_non_stream_request(self): r = requests.get('http://domain.com/') self.assertEqual(r.raw.read(), b'') class RememberCalledTest(unittest.TestCase): @staticmethod def several_calls(count, method, *args, **kwargs): results = [] for _ in range(count): results.append(method(*args, **kwargs)) return results def test_several_calls(self): with HTTMock(google_mock_count, facebook_mock_count): results = self.several_calls( 3, requests.get, 'http://facebook.com/') self.assertTrue(facebook_mock_count.call['called']) self.assertEqual(facebook_mock_count.call['count'], 3) self.assertFalse(google_mock_count.call['called']) self.assertEqual(google_mock_count.call['count'], 0) for r in results: self.assertEqual(r.content, b'Hello from Facebook') # Negative case: cleanup call data with HTTMock(facebook_mock_count): results = self.several_calls( 1, requests.get, 'http://facebook.com/') self.assertEquals(facebook_mock_count.call['count'], 1) @with_httmock(google_mock_count, facebook_mock_count) def test_several_call_decorated(self): results = self.several_calls(3, requests.get, 'http://facebook.com/') self.assertTrue(facebook_mock_count.call['called']) self.assertEqual(facebook_mock_count.call['count'], 3) self.assertFalse(google_mock_count.call['called']) self.assertEqual(google_mock_count.call['count'], 0) for r in results: self.assertEqual(r.content, b'Hello from Facebook') self.several_calls(1, requests.get, 'http://facebook.com/') self.assertEquals(facebook_mock_count.call['count'], 4)