Deprecate openstack repository

Remove all code and add a deprecation notice to the README. This should
make it really clear that we've moved.

Change-Id: Ic554437cf34fda7211ddfd2bb9d5f799f80a3096
This commit is contained in:
Jamie Lennox 2018-02-27 15:35:41 +11:00
parent 10a3470dac
commit 67663054a5
53 changed files with 9 additions and 4925 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
source = requests_mock
omit = requests_mock/tests/*
[report]
ignore_errors = True

50
.gitignore vendored
View File

@ -1,50 +0,0 @@
*.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
htmlcov
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
.idea
# Complexity
output/*.html
output/*/index.html
# Sphinx
doc/build
.testrepository
# Generated by PBR
AUTHORS
ChangeLog

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/requests-mock.git

View File

@ -1,4 +0,0 @@
[DEFAULT]
test_command=python -m subunit.run discover requests_mock $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -1,29 +0,0 @@
- project:
check:
jobs:
- requests-mock-keystoneclient-tip
- requests-mock-novaclient-tip
gate:
jobs:
- requests-mock-keystoneclient-tip
- requests-mock-novaclient-tip
- job:
name: requests-mock-keystoneclient-tip
parent: tox
description: |
Run test for requests-mock project.
Uses tox with the ``keystoneclient-tip`` environment.
vars:
tox_envlist: keystoneclient-tip
- job:
name: requests-mock-novaclient-tip
parent: tox
description: |
Run test for requests-mock project.
Uses tox with the ``novaclient-tip`` environment.
vars:
tox_envlist: novaclient-tip

180
LICENSE
View File

@ -1,180 +0,0 @@
Copyright (c) 2014, Jamie Lennox
All rights reserved.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@ -1,3 +0,0 @@
include AUTHORS
include ChangeLog
include LICENSE

View File

@ -1,86 +1,12 @@
===============================
requests-mock
===============================
=====================
REPOSITORY DEPRECATED
=====================
.. image:: https://badge.fury.io/py/requests-mock.png
:target: http://badge.fury.io/py/requests-mock
Hi, requests-mock development is continuing on github at
https://github.com/jamielennox/requests-mock
Intro
=====
Please submit your issues, patches and development there.
`requests-mock` provides a building block to stub out the HTTP `requests`_ portions of your testing code.
You should checkout the `docs`_ for more information.
The Basics
==========
Everything in `requests`_ eventually goes through an adapter to do the transport work.
`requests-mock` creates a custom `adapter` that allows you to predefine responses when certain URIs are called.
There are then a number of methods provided to get the adapter used.
A simple example:
.. code:: python
>>> import requests
>>> import requests_mock
>>> session = requests.Session()
>>> adapter = requests_mock.Adapter()
>>> session.mount('mock', adapter)
>>> adapter.register_uri('GET', 'mock://test.com', text='data')
>>> resp = session.get('mock://test.com')
>>> resp.status_code, resp.text
(200, 'data')
Obviously having all URLs be `mock://` prefixed isn't going to useful, so you can use `requests_mock.mock` to get the adapter into place.
As a context manager:
.. code:: python
>>> with requests_mock.mock() as m:
... m.get('http://test.com', text='data')
... requests.get('http://test.com').text
...
'data'
Or as a decorator:
.. code:: python
>>> @requests_mock.mock()
... def test_func(m):
... m.get('http://test.com', text='data')
... return requests.get('http://test.com').text
...
>>> test_func()
'data'
For more information checkout the `docs`_.
Reporting Bugs
==============
Please report all bugs on `LaunchPad`_.
License
=======
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.
.. _requests: http://python-requests.org
.. _docs: http://requests-mock.readthedocs.org
.. _LaunchPad: https://bugs.launchpad.net/requests-mock
The contents of this repository are still available in the Git source code
management system. To see the contents of this repository before it was
retired, please check out the previous commit with "git checkout HEAD^1".

View File

@ -1,3 +0,0 @@
coverage
sphinx
wheel

View File

@ -1,177 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/complexity.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/complexity.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/complexity"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/complexity"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

View File

@ -1,7 +0,0 @@
requests_mock
=============
.. toctree::
:maxdepth: 4
requests_mock

View File

@ -1,40 +0,0 @@
requests_mock package
=====================
Subpackages
-----------
.. toctree::
requests_mock.adapter module
----------------------------
.. automodule:: requests_mock.adapter
:members:
:undoc-members:
:show-inheritance:
requests_mock.exceptions module
-------------------------------
.. automodule:: requests_mock.exceptions
:members:
:undoc-members:
:show-inheritance:
requests_mock.fixture module
----------------------------
.. automodule:: requests_mock.contrib.fixture
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: requests_mock
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,242 +0,0 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end

View File

@ -1,21 +0,0 @@
=============
Adapter Usage
=============
Creating an Adapter
===================
The standard `requests`_ means of using an adapter is to :py:meth:`~requests.Session.mount` it on a created session. This is not the only way to load the adapter, however the same interactions will be used.
.. doctest::
>>> import requests
>>> import requests_mock
>>> session = requests.Session()
>>> adapter = requests_mock.Adapter()
>>> session.mount('mock', adapter)
At this point any requests made by the session to a URI starting with `mock://` will be sent to our adapter.
.. _requests: http://python-requests.org

View File

@ -1,263 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# complexity documentation build configuration file, created by
# sphinx-quickstart on Tue Jul 9 22:26:36 2013.
#
# This file is execfile()d with the current directory set to its containing dir
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import pbr.version
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..')))
import requests_mock # noqa
# -- General configuration ----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'requests-mock'
copyright = u'2014, Jamie Lennox'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
version_info = pbr.version.VersionInfo(project)
# The short X.Y version.
version = version_info.version_string()
# The full version, including alpha/beta/rc tags.
release = version_info.release_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build', 'api/setup.rst', 'api/requests_mock.tests.rst']
# The reST default role (used for this markup: `text`) to use for all documents
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'requests-mockdoc'
# -- Options for LaTeX output -------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'requests-mock.tex', u'Requests Mock Documentation',
u'Jamie Lennox', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output -------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'requests-mock', u'Requests Mock Documentation',
[u'Jamie Lennox'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'requests-mock', u'Requests Mock Documentation',
u'Jamie Lennox', 'requests-mock', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
requests_uri = 'http://docs.python-requests.org/en/latest/'
urllib3_uri = 'http://urllib3.readthedocs.org/en/latest'
python_uri = 'http://docs.python.org/3'
intersphinx_mapping = {'requests': (requests_uri, None),
'urllib': (python_uri, None),
'urllib3': (urllib3_uri, None),
'python': (python_uri, None)}

View File

@ -1,11 +0,0 @@
==================
Additional Loading
==================
Common additional loading mechanism are supported in the :py:mod:`requests_mock.contrib` module.
These modules may require dependencies outside of what is provided by `requests_mock` and so must be provided by the including application.
.. toctree::
fixture

View File

@ -1,42 +0,0 @@
========
Fixtures
========
`Fixtures`_ provide a way to create reusable state and helper methods in test cases.
To use the *requests-mock* fixture your tests need to have a dependency on the `fixtures`_ library.
This can be optionally installed when you install *requests-mock* by doing:
.. code:: shell
pip install requests-mock[fixture]
The fixture mocks the :py:meth:`requests.Session.get_adapter` method so that all requests will be served by the mock adapter.
The fixture provides the same interfaces as the adapter.
.. doctest::
>>> import requests
>>> from requests_mock.contrib import fixture
>>> import testtools
>>> class MyTestCase(testtools.TestCase):
...
... TEST_URL = 'http://www.google.com'
...
... def setUp(self):
... super(MyTestCase, self).setUp()
... self.requests_mock = self.useFixture(fixture.Fixture())
... self.requests_mock.register_uri('GET', self.TEST_URL, text='respA')
...
... def test_method(self):
... self.requests_mock.register_uri('POST', self.TEST_URL, text='respB')
... resp = requests.get(self.TEST_URL)
... self.assertEqual('respA', resp.text)
... self.assertEqual(self.TEST_URL, self.requests_mock.last_request.url)
...
.. _Fixtures: https://pypi.python.org/pypi/fixtures
.. _mock: https://pypi.python.org/pypi/mock

View File

@ -1,68 +0,0 @@
===============
Request History
===============
The object returned from creating a mock or registering a URI in an adapter is capable of tracking and querying the history of requests that this mock responded to.
Called
======
The easiest way to test if a request hit the adapter is to simply check the called property or the call_count property.
.. doctest::
>>> import requests
>>> import requests_mock
>>> with requests_mock.mock() as m:
... m.get('http://test.com, text='resp')
... resp = requests.get('http://test.com')
...
>>> m.called
True
>>> m.call_count
1
Request Objects
===============
The history of objects that passed through the `mocker`/`adapter` can also be retrieved
.. doctest::
>>> history = m.request_history
>>> len(history)
1
>>> history[0].method
'GET'
>>> history[0].url
'http://test.com/'
The alias `last_request` is also available for the last request to go through the mocker.
This request object is a wrapper around a standard :py:class:`requests.Request` object with some additional information that make the interface more workable (as the :py:class:`~requests.Request` object is generally not dealt with by users.
These additions include:
:text: The data of the request converted into a unicode string.
:json: The data of the request loaded from json into python objects.
:qs: The query string of the request. See :py:func:`urllib.parse.parse_qs` for information on the return format.
:hostname: The host name that the request was sent to.
:port: The port the request was sent to.
.. doctest::
>>> m.last_request.scheme
'http'
>>> m.last_request.netloc
'test.com'
The following parameters of the :py:func:`requests.request` call are also exposed via the request object:
:timeout: How long to wait for the server to send data before giving up.
:allow_redirects: Set to True if POST/PUT/DELETE redirect following is allowed.
:proxies: Dictionary mapping protocol to the URL of the proxy.
:verify: whether the SSL cert will be verified.
:cert: The client certificate or cert/key tuple for this request.
Note: That the default value of these attributes are the values that are passed to the adapter and not what is passed to the request method. This means that the default for allow_redirects is None (even though that is interpretted as True) if unset, whereas the defautl for verify is True, and the default for proxies the empty dict.

View File

@ -1,24 +0,0 @@
Welcome to requests-mock's documentation!
=========================================
Contents:
.. toctree::
:maxdepth: 2
overview
mocker
matching
response
knownissues
history
adapter
contrib
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,34 +0,0 @@
============
Known Issues
============
.. _case_insensitive:
Case Insensitivity
------------------
By default matching is done in a completely case insensitive way. This makes
sense for the protocol and host components which are defined as insensitive by
RFCs however it does not make sense for path.
A byproduct of this is that when using request history the values for path, qs
etc are all lowercased as this was what was used to do the matching.
To work around this when building an Adapter or Mocker you do
.. code:: python
with requests_mock.mock(case_sensitive=True) as m:
...
or you can override the default globally by
.. code:: python
requests_mock.mock.case_sensitive = True
It is recommended to run the global fix as it is intended that case sensitivity
will become the default in future releases.
Note that even with case_sensitive enabled the protocol and netloc of a mock
are still matched in a case insensitive way.

View File

@ -1,277 +0,0 @@
================
Request Matching
================
Whilst it is preferable to provide the whole URI to :py:meth:`requests_mock.Adapter.register_uri` it is possible to just specify components.
The examples in this file are loaded with:
.. doctest::
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. note::
The examples within use this syntax because request matching is a function of the adapter and not the mocker.
All the same arguments can be provided to the mocker if that is how you use `requests_mock` within your project, and use the
.. code:: python
mock.get(url, ...)
form in place of the given:
.. code:: python
adapter.register_uri('GET', url, ...)
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. note::
By default all matching is case insensitive. This can be adjusted by
passing case_sensitive=True when creating a mocker or adapter or globally
by doing:
.. code:: python
requests_mock.mock.case_sensitive = True
for more see: :ref:`case_insensitive`
Simple
======
The most simple way to match a request is to register the URL and method that will be requested with a textual response.
When a request is made that goes through the mocker this response will be retrieved.
.. doctest::
.. >>> adapter.register_uri('GET', 'mock://test.com/path', text='resp')
.. >>> session.get('mock://test.com/path').text
.. 'resp'
Path Matching
=============
You can specify a protocol-less path:
.. doctest::
.. >>> adapter.register_uri('GET', '//test.com/', text='resp')
.. >>> session.get('mock://test.com/').text
.. 'resp'
or you can specify just a path:
.. doctest::
.. >>> adapter.register_uri('GET', '/path', text='resp')
.. >>> session.get('mock://test.com/path').text
.. 'resp'
.. >>> session.get('mock://another.com/path').text
.. 'resp'
Query Strings
=============
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
Query strings provided to a register will match so long as at least those provided form part of the request.
.. doctest::
>>> adapter.register_uri('GET', '/7?a=1', text='resp')
>>> session.get('mock://test.com/7?a=1&b=2').text
'resp'
If any part of the query string is wrong then it will not match.
.. doctest::
>>> session.get('mock://test.com/7?a=3')
Traceback (most recent call last):
...
requests_mock.exceptions.NoMockAddress: No mock address: GET mock://test.com/7?a=3
This can be a problem in certain situations, so if you wish to match only the complete query string there is a flag `complete_qs`:
.. doctest::
>>> adapter.register_uri('GET', '/8?a=1', complete_qs=True, text='resp')
>>> session.get('mock://test.com/8?a=1&b=2')
Traceback (most recent call last):
...
requests_mock.exceptions.NoMockAddress: No mock address: GET mock://test.com/8?a=1&b=2
Matching ANY
============
There is a special symbol at `requests_mock.ANY` which acts as the wildcard to match anything.
It can be used as a replace for the method and/or the URL.
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. doctest::
>>> adapter.register_uri(requests_mock.ANY, 'mock://test.com/8', text='resp')
>>> session.get('mock://test.com/8').text
'resp'
>>> session.post('mock://test.com/8').text
'resp'
.. doctest::
>>> adapter.register_uri(requests_mock.ANY, requests_mock.ANY, text='resp')
>>> session.get('mock://whatever/you/like').text
'resp'
>>> session.post('mock://whatever/you/like').text
'resp'
Regular Expressions
===================
URLs can be specified with a regular expression using the python :py:mod:`re` module.
To use this you should pass an object created by :py:func:`re.compile`.
The URL is then matched using :py:meth:`re.regex.search` which means that it will match any component of the url, so if you want to match the start of a URL you will have to anchor it.
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. doctest::
.. >>> import re
.. >>> matcher = re.compile('tester.com/a')
.. >>> adapter.register_uri('GET', matcher, text='resp')
.. >>> session.get('mock://www.tester.com/a/b').text
.. 'resp'
If you use regular expression matching then *requests-mock* can't do it's normal query string or path only matching, that will need to be part of the expression.
Request Headers
===============
A dictionary of headers can be supplied such that the request will only match if the available headers also match.
Only the headers that are provided need match, any additional headers will be ignored.
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. doctest::
>>> adapter.register_uri('POST', 'mock://test.com/headers', request_headers={'key': 'val'}, text='resp')
>>> session.post('mock://test.com/headers', headers={'key': 'val', 'another': 'header'}).text
'resp'
>>> resp = session.post('mock://test.com/headers')
Traceback (most recent call last):
...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/headers
Additional Matchers
===================
As distinct from `Custom Matching` below we can add an additional matcher callback that lets us do more dynamic matching in addition to the standard options.
This is handled by a callback function that takes the request as a parameter:
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. doctest::
>>> def match_request_text(request):
... # request.text may be None, or '' prevents a TypeError.
... return 'hello' in (request.text or '')
...
>>> adapter.register_uri('POST', 'mock://test.com/additional', additional_matcher=match_request_text, text='resp')
>>> session.post('mock://test.com/headers', data='hello world').text
'resp'
>>> resp = session.post('mock://test.com/additional', data='goodbye world')
Traceback (most recent call last):
...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/additional
Using this mechanism lets you do custom handling such as parsing yaml or XML structures and matching on features of that data or anything else that is not directly handled via the provided matchers rather than build in every possible option to `requests_mock`.
Custom Matching
===============
Internally calling :py:meth:`~requests_mock.Adapter.register_uri` creates a *matcher* object for you and adds it to the list of matchers to check against.
A *matcher* is any callable that takes a :py:class:`requests.Request` and returns a :py:class:`requests.Response` on a successful match or *None* if it does not handle the request.
If you need more flexibility than provided by :py:meth:`~requests_mock.Adapter.register_uri` then you can add your own *matcher* to the :py:class:`~requests_mock.Adapter`. Custom *matchers* can be used in conjunction with the inbuilt *matchers*. If a matcher returns *None* then the request will be passed to the next *matcher* as with using :py:meth:`~requests_mock.Adapter.register_uri`.
.. doctest::
:hide:
>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock', adapter)
.. doctest::
>>> def custom_matcher(request):
... if request.path_url == '/test':
... resp = requests.Response()
... resp.status_code = 200
... return resp
... return None
...
>>> adapter.add_matcher(custom_matcher)
>>> session.get('mock://test.com/test').status_code
200
>>> session.get('mock://test.com/other')
Traceback (most recent call last):
...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/other

View File

@ -1,149 +0,0 @@
================
Using the Mocker
================
The mocker is a loading mechanism to ensure the adapter is correctly in place to intercept calls from requests.
It's goal is to provide an interface that is as close to the real requests library interface as possible.
Activation
==========
Loading of the Adapter is handled by the :py:class:`requests_mock.Mocker` class, which provides two ways to load an adapter:
Context Manager
---------------
The Mocker object can work as a context manager.
.. doctest::
>>> import requests
>>> import requests_mock
>>> with requests_mock.Mocker() as m:
... m.get('http://test.com', text='resp')
... requests.get('http://test.com').text
...
'resp'
Decorator
---------
Mocker can also be used as a decorator. The created object will then be passed as the last positional argument.
.. doctest::
>>> @requests_mock.Mocker()
... def test_function(m):
... m.get('http://test.com', text='resp')
... return requests.get('http://test.com').text
...
>>> test_function()
'resp'
If the position of the mock is likely to conflict with other arguments you can pass the `kw` argument to the Mocker to have the mocker object passed as that keyword argument instead.
.. doctest::
>>> @requests_mock.Mocker(kw='mock')
... def test_kw_function(**kwargs):
... kwargs['mock'].get('http://test.com', text='resp')
... return requests.get('http://test.com').text
...
>>> test_kw_function()
'resp'
Class Decorator
===============
Mocker can also be used to decorate a whole class. It works exactly like in case of decorating a normal function.
When used in this way they wrap every test method on the class. The mocker recognise methods that start with *test* as being test methods.
This is the same way that the `unittest.TestLoader` finds test methods by default.
It is possible that you want to use a different prefix for your tests. You can inform the mocker of the different prefix by setting `requests_mock.Mocker.TEST_PREFIX`:
.. doctest::
>>> requests_mock.Mocker.TEST_PREFIX = 'foo'
>>>
>>> @requests_mock.Mocker()
... class Thing(object):
... def foo_one(self, m):
... m.register_uri('GET', 'http://test.com', text='resp')
... return requests.get('http://test.com').text
... def foo_two(self, m):
... m.register_uri('GET', 'http://test.com', text='resp')
... return requests.get('http://test.com').text
...
>>>
>>> Thing().foo_one()
'resp'
>>> Thing().foo_two()
'resp'
This behavior mimics how patchers from `mock` library works.
Methods
=======
The mocker object can be used with a similar interface to requests itself.
.. doctest::
>>> with requests_mock.Mocker() as mock:
... mock.get('http://test.com', text='resp')
... requests.get('http://test.com').text
...
'resp'
The functions exist for the common HTTP method:
- :py:meth:`~requests_mock.MockerCore.delete`
- :py:meth:`~requests_mock.MockerCore.get`
- :py:meth:`~requests_mock.MockerCore.head`
- :py:meth:`~requests_mock.MockerCore.options`
- :py:meth:`~requests_mock.MockerCore.patch`
- :py:meth:`~requests_mock.MockerCore.post`
- :py:meth:`~requests_mock.MockerCore.put`
As well as the basic:
- :py:meth:`~requests_mock.MockerCore.request`
- :py:meth:`~requests_mock.MockerCore.register_uri`
These methods correspond to the HTTP method of your request, so to mock POST requests you would use the :py:meth:`~requests_mock.MockerCore.post` function.
Further information about what can be matched from a request can be found at :doc:`matching`
Real HTTP Requests
==================
The Mocker object takes the following parameters:
:real_http (bool): If True then any requests that are not handled by the mocking adapter will be forwarded to the real server. Defaults to False.
.. doctest::
>>> with requests_mock.Mocker(real_http=True) as m:
... m.register_uri('GET', 'http://test.com', text='resp')
... print(requests.get('http://test.com').text)
... print(requests.get('http://www.google.com').status_code) # doctest: +SKIP
...
'resp'
200
*New in 1.1*
Similarly when using a mocker you can register an individual URI to bypass the mocking infrastructure and make a real request. Note this only works when using the mocker and not when directly mounting an adapter.
.. doctest::
>>> with requests_mock.Mocker() as m:
... m.register_uri('GET', 'http://test.com', text='resp')
... m.register_uri('GET', 'http://www.google.com', real_http=True)
... print(requests.get('http://test.com').text)
... print(requests.get('http://www.google.com').status_code) # doctest: +SKIP
...
'resp'
200

View File

@ -1,15 +0,0 @@
========
Overview
========
The `requests`_ library has the concept of `pluggable transport adapters`_.
These adapters allow you to register your own handlers for different URIs or protocols.
The *requests-mock* library at its core is simply a transport adapter that can be preloaded with responses that are returned if certain URIs are requested.
This is particularly useful in unit tests where you want to return known responses from HTTP requests without making actual calls.
As the `requests`_ library has very limited options for how to load and use adapters *requests-mock* also provides a number of ways to make sure the mock adapter is used.
These are only loading mechanisms, they do not contain any logic and can be used as a reference to load the adapter in whatever ways works best for your project.
.. _requests: http://python-requests.org
.. _pluggable transport adapters: http://docs.python-requests.org/en/latest/user/advanced/#transport-adapters

View File

@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@ -1,179 +0,0 @@
==================
Creating Responses
==================
.. note::
The examples within use this syntax because response creation is a function of the adapter and not the mocker.
All the same arguments can be provided to the mocker if that is how you use `requests_mock` within your project, and use the
.. code:: python
mock.get(url, ...)
form in place of the given:
.. code:: python
adapter.register_uri('GET', url, ...)
Registering Responses
=====================
Responses are registered with the :py:meth:`requests_mock.Adapter.register_uri` function on the adapter.
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com', text='Success')
>>> resp = session.get('mock://test.com')
>>> resp.text
'Success'
:py:meth:`~requests_mock.Adapter.register_uri` takes the HTTP method, the URI and then information that is used to build the response. This information includes:
:status_code: The HTTP status response to return. Defaults to 200.
:reason: The reason text that accompanies the Status (e.g. 'OK' in '200 OK')
:headers: A dictionary of headers to be included in the response.
:cookies: A CookieJar containing all the cookies to add to the response.
To specify the body of the response there are a number of options that depend on the format that you wish to return.
:json: A python object that will be converted to a JSON string.
:text: A unicode string. This is typically what you will want to use for regular textual content.
:content: A byte string. This should be used for including binary data in responses.
:body: A file like object that contains a `.read()` function.
:raw: A prepopulated :py:class:`urllib3.response.HTTPResponse` to be returned.
:exc: An exception that will be raised instead of returning a response.
These options are named to coincide with the parameters on a :py:class:`requests.Response` object. For example:
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com/1', json={'a': 'b'}, status_code=200)
>>> resp = session.get('mock://test.com/1')
>>> resp.json()
{'a': 'b'}
>>> adapter.register_uri('GET', 'mock://test.com/2', text='Not Found', status_code=404)
>>> resp = session.get('mock://test.com/2')
>>> resp.text
'Not Found'
>>> resp.status_code
404
It only makes sense to provide at most one body element per response.
Dynamic Response
================
A callback can be provided in place of any of the body elements.
Callbacks must be a function in the form of
.. code:: python
def callback(request, context):
and return a value suitable to the body element that was specified.
The elements provided are:
:request: The :py:class:`requests.Request` object that was provided.
:context: An object containing the collected known data about this response.
The available properties on the `context` are:
:headers: The dictionary of headers that are to be returned in the response.
:status_code: The status code that is to be returned in the response.
:reason: The string HTTP status code reason that is to be returned in the response.
:cookies: A :py:class:`requests_mock.CookieJar` of cookies that will be merged into the response.
These parameters are populated initially from the variables provided to the :py:meth:`~requests_mock.Adapter.register_uri` function and if they are modified on the context object then those changes will be reflected in the response.
.. doctest::
>>> def text_callback(request, context):
... context.status_code = 200
... context.headers['Test1'] = 'value1'
... return 'response'
...
>>> adapter.register_uri('GET',
... 'mock://test.com/3',
... text=text_callback,
... headers={'Test2': 'value2'},
... status_code=400)
>>> resp = session.get('mock://test.com/3')
>>> resp.status_code, resp.headers, resp.text
(200, {'Test1': 'value1', 'Test2': 'value2'}, 'response')
Response Lists
==============
Multiple responses can be provided to be returned in order by specifying the keyword parameters in a list.
If the list is exhausted then the last response will continue to be returned.
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com/4', [{'text': 'resp1', 'status_code': 300},
... {'text': 'resp2', 'status_code': 200}])
>>> resp = session.get('mock://test.com/4')
>>> (resp.status_code, resp.text)
(300, 'resp1')
>>> resp = session.get('mock://test.com/4')
>>> (resp.status_code, resp.text)
(200, 'resp2')
>>> resp = session.get('mock://test.com/4')
>>> (resp.status_code, resp.text)
(200, 'resp2')
Callbacks work within response lists in exactly the same way they do normally;
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com/5', [{'text': text_callback}]),
>>> resp = session.get('mock://test.com/5')
>>> resp.status_code, resp.headers, resp.text
(200, {'Test1': 'value1', 'Test2': 'value2'}, 'response')
Raising Exceptions
==================
When trying to emulate a connection timeout or SSLError you need to be able to throw an exception when a mock is hit.
This can be achieved by passing the `exc` parameter instead of a body parameter.
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com/6', exc=requests.exceptions.ConnectTimeout),
>>> session.get('mock://test.com/6')
Traceback (most recent call last):
...
ConnectTimeout:
Handling Cookies
================
Whilst cookies are just headers they are treated in a different way, both in HTTP and the requests library.
To work as closely to the requests library as possible there are two ways to provide cookies to requests_mock responses.
The most simple method is to use a dictionary interface.
The Key and value of the dictionary are turned directly into the name and value of the cookie.
This method does not allow you to set any of the more advanced cookie parameters like expiry or domain.
.. doctest::
>>> adapter.register_uri('GET', 'mock://test.com/7', cookies={'foo': 'bar'}),
>>> resp = session.get('mock://test.com/7')
>>> resp.cookies['foo']
'bar'
The more advanced way is to construct and populate a cookie jar that you can add cookies to and pass that to the mocker.
.. doctest::
>>> jar = requests_mock.CookieJar()
>>> jar.set('foo', 'bar', domain='.test.com', path='/baz')
>>> adapter.register_uri('GET', 'mock://test.com/8', cookies=jar),
>>> resp = session.get('mock://test.com/8')
>>> resp.cookies['foo']
'bar'
>>> resp.cookies.list_paths()
['/baz']

View File

@ -1,6 +0,0 @@
---
prelude: >
Add a called_once property to the mockers.
features:
- A called_once property was added to the adapter and the mocker. This gives
us an easy way to emulate mock's assert_called_once.

View File

@ -1,4 +0,0 @@
---
features:
- Allow specifying an `additional_matcher` to the mocker that will call a
function to allow a user to add their own custom request matching logic.

View File

@ -1,17 +0,0 @@
---
prelude: >
It is now possible to make URL matching and request history not lowercase
the provided URLs.
features:
- You can pass case_sensitive=True to an adapter or set
`requests_mock.mock.case_sensitive = True` globally to enable case
sensitive matching.
upgrade:
- It is recommended you add `requests_mock.mock.case_sensitive = True` to
your base test file to globally turn on case sensitive matching as this
will become the default in a 2.X release.
fixes:
- Reported in bug \#1584008 all request matching is done in a case
insensitive way, as a byproduct of this request history is handled in a
case insensitive way. This can now be controlled by setting case_sensitive
to True when creating an adapter or globally.

View File

@ -1,7 +0,0 @@
---
prelude: >
Installing the requirements for the 'fixture' contrib package can now be
done via pip with `pip install requests-mock[fixture]`
features:
Added 'fixture' to pip extras so you can install the fixture requirements
with `pip install requests-mock[fixture]`

View File

@ -1,37 +0,0 @@
# 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.
from requests_mock.adapter import Adapter, ANY
from requests_mock.exceptions import MockException, NoMockAddress
from requests_mock.mocker import mock, Mocker, MockerCore
from requests_mock.mocker import DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
from requests_mock.response import create_response, CookieJar
__all__ = ['Adapter',
'ANY',
'create_response',
'CookieJar',
'mock',
'Mocker',
'MockerCore',
'MockException',
'NoMockAddress',
'DELETE',
'GET',
'HEAD',
'OPTIONS',
'PATCH',
'POST',
'PUT',
]

View File

@ -1,287 +0,0 @@
# 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.
import weakref
from requests.adapters import BaseAdapter
import six
from six.moves.urllib import parse as urlparse
from requests_mock import exceptions
from requests_mock.request import _RequestObjectProxy
from requests_mock.response import _MatcherResponse
ANY = object()
class _RequestHistoryTracker(object):
def __init__(self):
self.request_history = []
def _add_to_history(self, request):
self.request_history.append(request)
@property
def last_request(self):
"""Retrieve the latest request sent"""
try:
return self.request_history[-1]
except IndexError:
return None
@property
def called(self):
return self.call_count > 0
@property
def called_once(self):
return self.call_count == 1
@property
def call_count(self):
return len(self.request_history)
class _RunRealHTTP(Exception):
"""A fake exception to jump out of mocking and allow a real request.
This exception is caught at the mocker level and allows it to execute this
request through the real requests mechanism rather than the mocker.
It should never be exposed to a user.
"""
class _Matcher(_RequestHistoryTracker):
"""Contains all the information about a provided URL to match."""
def __init__(self, method, url, responses, complete_qs, request_headers,
additional_matcher, real_http, case_sensitive):
"""
:param bool complete_qs: Match the entire query string. By default URLs
match if all the provided matcher query arguments are matched and
extra query arguments are ignored. Set complete_qs to true to
require that the entire query string needs to match.
"""
super(_Matcher, self).__init__()
self._method = method
self._url = url
self._responses = responses
self._complete_qs = complete_qs
self._request_headers = request_headers
self._real_http = real_http
self._additional_matcher = additional_matcher
# url can be a regex object or ANY so don't always run urlparse
if isinstance(url, six.string_types):
url_parts = urlparse.urlparse(url)
self._scheme = url_parts.scheme.lower()
self._netloc = url_parts.netloc.lower()
self._path = url_parts.path or '/'
self._query = url_parts.query
if not case_sensitive:
self._path = self._path.lower()
self._query = self._query.lower()
else:
self._scheme = None
self._netloc = None
self._path = None
self._query = None
def _match_method(self, request):
if self._method is ANY:
return True
if request.method.lower() == self._method.lower():
return True
return False
def _match_url(self, request):
if self._url is ANY:
return True
# regular expression matching
if hasattr(self._url, 'search'):
return self._url.search(request.url) is not None
# scheme is always matched case insensitive
if self._scheme and request.scheme.lower() != self._scheme:
return False
# netloc is always matched case insensitive
if self._netloc and request.netloc.lower() != self._netloc:
return False
if (request.path or '/') != self._path:
return False
# construct our own qs structure as we remove items from it below
request_qs = urlparse.parse_qs(request.query)
matcher_qs = urlparse.parse_qs(self._query)
for k, vals in six.iteritems(matcher_qs):
for v in vals:
try:
request_qs.get(k, []).remove(v)
except ValueError:
return False
if self._complete_qs:
for v in six.itervalues(request_qs):
if v:
return False
return True
def _match_headers(self, request):
for k, vals in six.iteritems(self._request_headers):
try:
header = request.headers[k]
except KeyError:
# NOTE(jamielennox): This seems to be a requests 1.2/2
# difference, in 2 they are just whatever the user inputted in
# 1 they are bytes. Let's optionally handle both and look at
# removing this when we depend on requests 2.
if not isinstance(k, six.text_type):
return False
try:
header = request.headers[k.encode('utf-8')]
except KeyError:
return False
if header != vals:
return False
return True
def _match_additional(self, request):
if callable(self._additional_matcher):
return self._additional_matcher(request)
if self._additional_matcher is not None:
raise TypeError("Unexpected format of additional matcher.")
return True
def _match(self, request):
return (self._match_method(request) and
self._match_url(request) and
self._match_headers(request) and
self._match_additional(request))
def __call__(self, request):
if not self._match(request):
return None
# doing this before _add_to_history means real requests are not stored
# in the request history. I'm not sure what is better here.
if self._real_http:
raise _RunRealHTTP()
if len(self._responses) > 1:
response_matcher = self._responses.pop(0)
else:
response_matcher = self._responses[0]
self._add_to_history(request)
return response_matcher.get_response(request)
class Adapter(BaseAdapter, _RequestHistoryTracker):
"""A fake adapter than can return predefined responses.
"""
def __init__(self, case_sensitive=False):
super(Adapter, self).__init__()
self._case_sensitive = case_sensitive
self._matchers = []
def send(self, request, **kwargs):
request = _RequestObjectProxy(request,
case_sensitive=self._case_sensitive,
**kwargs)
self._add_to_history(request)
for matcher in reversed(self._matchers):
try:
resp = matcher(request)
except Exception:
request._matcher = weakref.ref(matcher)
raise
if resp is not None:
request._matcher = weakref.ref(matcher)
resp.connection = self
return resp
raise exceptions.NoMockAddress(request)
def close(self):
pass
def register_uri(self, method, url, response_list=None, **kwargs):
"""Register a new URI match and fake response.
:param str method: The HTTP method to match.
:param str url: The URL to match.
"""
complete_qs = kwargs.pop('complete_qs', False)
additional_matcher = kwargs.pop('additional_matcher', None)
request_headers = kwargs.pop('request_headers', {})
real_http = kwargs.pop('_real_http', False)
if response_list and kwargs:
raise RuntimeError('You should specify either a list of '
'responses OR response kwargs. Not both.')
elif real_http and (response_list or kwargs):
raise RuntimeError('You should specify either response data '
'OR real_http. Not both.')
elif not response_list:
response_list = [] if real_http else [kwargs]
# NOTE(jamielennox): case_sensitive is not present as a kwarg because i
# think there would be an edge case where the adapter and register_uri
# had different values.
# Ideally case_sensitive would be a value passed to match() however
# this would change the contract of matchers so we pass ito to the
# proxy and the matcher separately.
responses = [_MatcherResponse(**k) for k in response_list]
matcher = _Matcher(method,
url,
responses,
case_sensitive=self._case_sensitive,
complete_qs=complete_qs,
additional_matcher=additional_matcher,
request_headers=request_headers,
real_http=real_http)
self.add_matcher(matcher)
return matcher
def add_matcher(self, matcher):
"""Register a custom matcher.
A matcher is a callable that takes a `requests.Request` and returns a
`requests.Response` if it matches or None if not.
:param callable matcher: The matcher to execute.
"""
self._matchers.append(matcher)
__all__ = ['Adapter']

View File

@ -1,60 +0,0 @@
# 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.
import requests
def _versiontuple(v):
return tuple(map(int, (v.split("."))))
_requests_version = _versiontuple(requests.__version__)
class _FakeHTTPMessage(object):
def __init__(self, headers):
self.headers = headers
def getheaders(self, name):
try:
return [self.headers[name]]
except KeyError:
return []
def get_all(self, name, failobj=None):
# python 3 only, overrides email.message.Message.get_all
try:
return [self.headers[name]]
except KeyError:
return failobj
class _FakeHTTPResponse(object):
def __init__(self, headers):
self.msg = _FakeHTTPMessage(headers)
def isclosed(self):
# Don't let urllib try to close me
return False
if _requests_version < (2, 3):
# NOTE(jamielennox): There is a problem with requests < 2.3.0 such that it
# needs a httplib message for use with cookie extraction. It has been fixed
# but it is needed until we can rely on a recent enough requests version.
_fake_http_response = _FakeHTTPResponse({})
else:
_fake_http_response = None

View File

@ -1,27 +0,0 @@
# 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.
import fixtures
from requests_mock import mocker
class Fixture(fixtures.Fixture, mocker.MockerCore):
def __init__(self, **kwargs):
fixtures.Fixture.__init__(self)
mocker.MockerCore.__init__(self, **kwargs)
def setUp(self):
super(Fixture, self).setUp()
self.start()
self.addCleanup(self.stop)

View File

@ -1,30 +0,0 @@
# 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.
class MockException(Exception):
"""Base Exception for library"""
class NoMockAddress(MockException):
"""The requested URL was not mocked"""
def __init__(self, request):
self.request = request
def __str__(self):
return "No mock address: %s %s" % (self.request.method,
self.request.url)
class InvalidRequest(MockException):
"""This call cannot be made under a mocked environment"""

View File

@ -1,248 +0,0 @@
# 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.
import functools
import requests
from requests_mock import adapter
from requests_mock import exceptions
DELETE = 'DELETE'
GET = 'GET'
HEAD = 'HEAD'
OPTIONS = 'OPTIONS'
PATCH = 'PATCH'
POST = 'POST'
PUT = 'PUT'
_original_send = requests.Session.send
class MockerCore(object):
"""A wrapper around common mocking functions.
Automate the process of mocking the requests library. This will keep the
same general options available and prevent repeating code.
"""
_PROXY_FUNCS = set(['last_request',
'add_matcher',
'request_history',
'called',
'called_once',
'call_count'])
case_sensitive = False
"""case_sensitive handles a backwards incompatible bug. The URL used to
match against our matches and that is saved in request_history is always
lowercased. This is incorrect as it reports incorrect history to the user
and doesn't allow case sensitive path matching.
Unfortunately fixing this change is backwards incompatible in the 1.X
series as people may rely on this behaviour. To work around this you can
globally set:
requests_mock.mock.case_sensitive = True
which will prevent the lowercase being executed and return case sensitive
url and query information.
This will become the default in a 2.X release. See bug: #1584008.
"""
def __init__(self, **kwargs):
self.case_sensitive = kwargs.pop('case_sensitive', self.case_sensitive)
self._adapter = (
kwargs.pop('adapter', None) or
adapter.Adapter(case_sensitive=self.case_sensitive)
)
self._real_http = kwargs.pop('real_http', False)
self._last_send = None
if kwargs:
raise TypeError('Unexpected Arguments: %s' % ', '.join(kwargs))
def start(self):
"""Start mocking requests.
Install the adapter and the wrappers required to intercept requests.
"""
if self._last_send:
raise RuntimeError('Mocker has already been started')
self._last_send = requests.Session.send
def _fake_get_adapter(session, url):
return self._adapter
def _fake_send(session, request, **kwargs):
real_get_adapter = requests.Session.get_adapter
requests.Session.get_adapter = _fake_get_adapter
# NOTE(jamielennox): self._last_send vs _original_send. Whilst it
# seems like here we would use _last_send there is the possibility
# that the user has messed up and is somehow nesting their mockers.
# If we call last_send at this point then we end up calling this
# function again and the outer level adapter ends up winning.
# All we really care about here is that our adapter is in place
# before calling send so we always jump directly to the real
# function so that our most recently patched send call ends up
# putting in the most recent adapter. It feels funny, but it works.
try:
return _original_send(session, request, **kwargs)
except exceptions.NoMockAddress:
if not self._real_http:
raise
except adapter._RunRealHTTP:
# this mocker wants you to run the request through the real
# requests library rather than the mocking. Let it.
pass
finally:
requests.Session.get_adapter = real_get_adapter
return _original_send(session, request, **kwargs)
requests.Session.send = _fake_send
def stop(self):
"""Stop mocking requests.
This should have no impact if mocking has not been started.
"""
if self._last_send:
requests.Session.send = self._last_send
self._last_send = None
def __getattr__(self, name):
if name in self._PROXY_FUNCS:
try:
return getattr(self._adapter, name)
except AttributeError:
pass
raise AttributeError(name)
def register_uri(self, *args, **kwargs):
# you can pass real_http here, but it's private to pass direct to the
# adapter, because if you pass direct to the adapter you'll see the exc
kwargs['_real_http'] = kwargs.pop('real_http', False)
return self._adapter.register_uri(*args, **kwargs)
def request(self, *args, **kwargs):
return self.register_uri(*args, **kwargs)
def get(self, *args, **kwargs):
return self.request(GET, *args, **kwargs)
def options(self, *args, **kwargs):
return self.request(OPTIONS, *args, **kwargs)
def head(self, *args, **kwargs):
return self.request(HEAD, *args, **kwargs)
def post(self, *args, **kwargs):
return self.request(POST, *args, **kwargs)
def put(self, *args, **kwargs):
return self.request(PUT, *args, **kwargs)
def patch(self, *args, **kwargs):
return self.request(PATCH, *args, **kwargs)
def delete(self, *args, **kwargs):
return self.request(DELETE, *args, **kwargs)
class Mocker(MockerCore):
"""The standard entry point for mock Adapter loading.
"""
#: Defines with what should method name begin to be patched
TEST_PREFIX = 'test'
def __init__(self, **kwargs):
"""Create a new mocker adapter.
:param str kw: Pass the mock object through to the decorated function
as this named keyword argument, rather than a positional argument.
:param bool real_http: True to send the request to the real requested
uri if there is not a mock installed for it. Defaults to False.
"""
self._kw = kwargs.pop('kw', None)
super(Mocker, self).__init__(**kwargs)
def __enter__(self):
self.start()
return self
def __exit__(self, type, value, traceback):
self.stop()
def __call__(self, obj):
if isinstance(obj, type):
return self.decorate_class(obj)
return self.decorate_callable(obj)
def copy(self):
"""Returns an exact copy of current mock
"""
m = Mocker(
kw=self._kw,
real_http=self._real_http,
case_sensitive=self.case_sensitive
)
return m
def decorate_callable(self, func):
"""Decorates a callable
:param callable func: callable to decorate
"""
@functools.wraps(func)
def inner(*args, **kwargs):
with self.copy() as m:
if self._kw:
kwargs[self._kw] = m
else:
args = list(args)
args.append(m)
return func(*args, **kwargs)
return inner
def decorate_class(self, klass):
"""Decorates methods in a class with request_mock
Method will be decorated only if it name begins with `TEST_PREFIX`
:param object klass: class which methods will be decorated
"""
for attr_name in dir(klass):
if not attr_name.startswith(self.TEST_PREFIX):
continue
attr = getattr(klass, attr_name)
if not hasattr(attr, '__call__'):
continue
m = self.copy()
setattr(klass, attr_name, m(attr))
return klass
mock = Mocker

View File

@ -1,157 +0,0 @@
# 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.
import copy
import json
import requests
import six
from six.moves.urllib import parse as urlparse
class _RequestObjectProxy(object):
"""A wrapper around a requests.Request that gives some extra information.
This will be important both for matching and so that when it's save into
the request_history users will be able to access these properties.
"""
def __init__(self, request, **kwargs):
self._request = request
self._matcher = None
self._url_parts_ = None
self._qs = None
# All of these params should always exist but we use a default
# to make the test setup easier.
self._timeout = kwargs.pop('timeout', None)
self._allow_redirects = kwargs.pop('allow_redirects', None)
self._verify = kwargs.pop('verify', None)
self._cert = kwargs.pop('cert', None)
self._proxies = copy.deepcopy(kwargs.pop('proxies', {}))
# FIXME(jamielennox): This is part of bug #1584008 and should default
# to True (or simply removed) in a major version bump.
self._case_sensitive = kwargs.pop('case_sensitive', False)
def __getattr__(self, name):
return getattr(self._request, name)
@property
def _url_parts(self):
if self._url_parts_ is None:
url = self._request.url
if not self._case_sensitive:
url = url.lower()
self._url_parts_ = urlparse.urlparse(url)
return self._url_parts_
@property
def scheme(self):
return self._url_parts.scheme
@property
def netloc(self):
return self._url_parts.netloc
@property
def hostname(self):
try:
return self.netloc.split(':')[0]
except IndexError:
return ''
@property
def port(self):
components = self.netloc.split(':')
try:
return int(components[1])
except (IndexError, ValueError):
pass
if self.scheme == 'https':
return 443
if self.scheme == 'http':
return 80
# The default return shouldn't matter too much because if you are
# wanting to test this value you really should be explicitly setting it
# somewhere. 0 at least is a boolean False and an int.
return 0
@property
def path(self):
return self._url_parts.path
@property
def query(self):
return self._url_parts.query
@property
def qs(self):
if self._qs is None:
self._qs = urlparse.parse_qs(self.query)
return self._qs
@property
def timeout(self):
return self._timeout
@property
def allow_redirects(self):
return self._allow_redirects
@property
def verify(self):
return self._verify
@property
def cert(self):
return self._cert
@property
def proxies(self):
return self._proxies
@classmethod
def _create(cls, *args, **kwargs):
return cls(requests.Request(*args, **kwargs).prepare())
@property
def text(self):
body = self.body
if isinstance(body, six.binary_type):
body = body.decode('utf-8')
return body
def json(self, **kwargs):
return json.loads(self.text, **kwargs)
@property
def matcher(self):
"""The matcher that this request was handled by.
The matcher object is handled by a weakref. It will return the matcher
object if it is still available - so if the mock is still in place. If
the matcher is not available it will return None.
"""
return self._matcher()
def __str__(self):
return "{0.method} {0.url}".format(self._request)

View File

@ -1,247 +0,0 @@
# 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.
import json as jsonutils
from requests.adapters import HTTPAdapter
from requests.cookies import MockRequest, MockResponse
from requests.cookies import RequestsCookieJar
from requests.cookies import merge_cookies, cookiejar_from_dict
from requests.packages.urllib3.response import HTTPResponse
import six
from requests_mock import compat
from requests_mock import exceptions
_BODY_ARGS = frozenset(['raw', 'body', 'content', 'text', 'json'])
_HTTP_ARGS = frozenset(['status_code', 'reason', 'headers', 'cookies'])
_DEFAULT_STATUS = 200
_http_adapter = HTTPAdapter()
class CookieJar(RequestsCookieJar):
def set(self, name, value, **kwargs):
"""Add a cookie to the Jar.
:param str name: cookie name/key.
:param str value: cookie value.
:param int version: Integer or None. Netscape cookies have version 0.
RFC 2965 and RFC 2109 cookies have a version cookie-attribute of 1.
However, note that cookielib may 'downgrade' RFC 2109 cookies to
Netscape cookies, in which case version is 0.
:param str port: String representing a port or a set of ports
(eg. '80', or '80,8080'),
:param str domain: The domain the cookie should apply to.
:param str path: Cookie path (a string, eg. '/acme/rocket_launchers').
:param bool secure: True if cookie should only be returned over a
secure connection.
:param int expires: Integer expiry date in seconds since epoch or None.
:param bool discard: True if this is a session cookie.
:param str comment: String comment from the server explaining the
function of this cookie.
:param str comment_url: URL linking to a comment from the server
explaining the function of this cookie.
"""
# just here to provide the function documentation
return super(CookieJar, self).set(name, value, **kwargs)
def _check_body_arguments(**kwargs):
# mutual exclusion, only 1 body method may be provided
provided = [x for x in _BODY_ARGS if kwargs.pop(x, None) is not None]
if len(provided) > 1:
raise RuntimeError('You may only supply one body element. You '
'supplied %s' % ', '.join(provided))
extra = [x for x in kwargs if x not in _HTTP_ARGS]
if extra:
raise TypeError('Too many arguments provided. Unexpected '
'arguments %s.' % ', '.join(extra))
class _FakeConnection(object):
"""An object that can mock the necessary parts of a socket interface."""
def send(self, request, **kwargs):
msg = 'This response was created without a connection. You are ' \
'therefore unable to make a request directly on that connection.'
raise exceptions.InvalidRequest(msg)
def close(self):
pass
def _extract_cookies(request, response, cookies):
"""Add cookies to the response.
Cookies in requests are extracted from the headers in the original_response
httplib.HTTPMessage which we don't create so we have to do this step
manually.
"""
# This will add cookies set manually via the Set-Cookie or Set-Cookie2
# header but this only allows 1 cookie to be set.
http_message = compat._FakeHTTPMessage(response.headers)
response.cookies.extract_cookies(MockResponse(http_message),
MockRequest(request))
# This allows you to pass either a CookieJar or a dictionary to request_uri
# or directly to create_response. To allow more than one cookie to be set.
if cookies:
merge_cookies(response.cookies, cookies)
class _IOReader(six.BytesIO):
"""A reader that makes a BytesIO look like a HTTPResponse.
A HTTPResponse will return an empty string when you read from it after
the socket has been closed. A BytesIO will raise a ValueError. For
compatibility we want to do the same thing a HTTPResponse does.
"""
def read(self, *args, **kwargs):
if self.closed:
return six.b('')
# not a new style object in python 2
return six.BytesIO.read(self, *args, **kwargs)
def create_response(request, **kwargs):
"""
:param int status_code: The status code to return upon a successful
match. Defaults to 200.
:param HTTPResponse raw: A HTTPResponse object to return upon a
successful match.
:param io.IOBase body: An IO object with a read() method that can
return a body on successful match.
:param bytes content: A byte string to return upon a successful match.
:param unicode text: A text string to return upon a successful match.
:param object json: A python object to be converted to a JSON string
and returned upon a successful match.
:param dict headers: A dictionary object containing headers that are
returned upon a successful match.
:param CookieJar cookies: A cookie jar with cookies to set on the
response.
"""
connection = kwargs.pop('connection', _FakeConnection())
_check_body_arguments(**kwargs)
raw = kwargs.pop('raw', None)
body = kwargs.pop('body', None)
content = kwargs.pop('content', None)
text = kwargs.pop('text', None)
json = kwargs.pop('json', None)
encoding = None
if content is not None and not isinstance(content, six.binary_type):
raise TypeError('Content should be binary data')
if text is not None and not isinstance(text, six.string_types):
raise TypeError('Text should be string data')
if json is not None:
text = jsonutils.dumps(json)
if text is not None:
encoding = 'utf-8'
content = text.encode(encoding)
if content is not None:
body = _IOReader(content)
if not raw:
raw = HTTPResponse(status=kwargs.get('status_code', _DEFAULT_STATUS),
headers=kwargs.get('headers', {}),
reason=kwargs.get('reason'),
body=body or _IOReader(six.b('')),
decode_content=False,
preload_content=False,
original_response=compat._fake_http_response)
response = _http_adapter.build_response(request, raw)
response.connection = connection
response.encoding = encoding
_extract_cookies(request, response, kwargs.get('cookies'))
return response
class _Context(object):
"""Stores the data being used to process a current URL match."""
def __init__(self, headers, status_code, reason, cookies):
self.headers = headers
self.status_code = status_code
self.reason = reason
self.cookies = cookies
class _MatcherResponse(object):
def __init__(self, **kwargs):
self._exc = kwargs.pop('exc', None)
# If the user is asking for an exception to be thrown then prevent them
# specifying any sort of body or status response as it won't be used.
# This may be protecting the user too much but can be removed later.
if self._exc and kwargs:
raise TypeError('Cannot provide other arguments with exc.')
_check_body_arguments(**kwargs)
self._params = kwargs
# whilst in general you shouldn't do type checking in python this
# makes sure we don't end up with differences between the way types
# are handled between python 2 and 3.
content = self._params.get('content')
text = self._params.get('text')
if content is not None and not (callable(content) or
isinstance(content, six.binary_type)):
raise TypeError('Content should be a callback or binary data')
if text is not None and not (callable(text) or
isinstance(text, six.string_types)):
raise TypeError('Text should be a callback or string data')
def get_response(self, request):
# if an error was requested then raise that instead of doing response
if self._exc:
raise self._exc
# If a cookie dict is passed convert it into a CookieJar so that the
# cookies object available in a callback context is always a jar.
cookies = self._params.get('cookies', CookieJar())
if isinstance(cookies, dict):
cookies = cookiejar_from_dict(cookies, CookieJar())
context = _Context(self._params.get('headers', {}).copy(),
self._params.get('status_code', _DEFAULT_STATUS),
self._params.get('reason'),
cookies)
# if a body element is a callback then execute it
def _call(f, *args, **kwargs):
return f(request, context, *args, **kwargs) if callable(f) else f
return create_response(request,
json=_call(self._params.get('json')),
text=_call(self._params.get('text')),
content=_call(self._params.get('content')),
body=_call(self._params.get('body')),
raw=self._params.get('raw'),
status_code=context.status_code,
reason=context.reason,
headers=context.headers,
cookies=context.cookies)

View File

@ -1,17 +0,0 @@
# 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.
import testtools
class TestCase(testtools.TestCase):
pass

View File

@ -1,652 +0,0 @@
# 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.
import json
import re
import requests
import six
from six.moves.urllib import parse as urlparse
import requests_mock
from requests_mock.tests import base
class MyExc(Exception):
pass
class SessionAdapterTests(base.TestCase):
PREFIX = "mock"
def setUp(self):
super(SessionAdapterTests, self).setUp()
self.adapter = requests_mock.Adapter()
self.session = requests.Session()
self.session.mount(self.PREFIX, self.adapter)
self.url = '%s://example.com/test' % self.PREFIX
self.headers = {'header_a': 'A', 'header_b': 'B'}
def assertHeaders(self, resp):
for k, v in six.iteritems(self.headers):
self.assertEqual(v, resp.headers[k])
def assertLastRequest(self, method='GET', body=None):
self.assertEqual(self.url, self.adapter.last_request.url)
self.assertEqual(method, self.adapter.last_request.method)
self.assertEqual(body, self.adapter.last_request.body)
url_parts = urlparse.urlparse(self.url)
qs = urlparse.parse_qs(url_parts.query)
self.assertEqual(url_parts.scheme, self.adapter.last_request.scheme)
self.assertEqual(url_parts.netloc, self.adapter.last_request.netloc)
self.assertEqual(url_parts.path, self.adapter.last_request.path)
self.assertEqual(url_parts.query, self.adapter.last_request.query)
self.assertEqual(url_parts.query, self.adapter.last_request.query)
self.assertEqual(qs, self.adapter.last_request.qs)
def test_content(self):
data = six.b('testdata')
self.adapter.register_uri('GET',
self.url,
content=data,
headers=self.headers)
resp = self.session.get(self.url)
self.assertEqual(data, resp.content)
self.assertHeaders(resp)
self.assertLastRequest()
def test_content_callback(self):
status_code = 401
data = six.b('testdata')
def _content_cb(request, context):
context.status_code = status_code
context.headers.update(self.headers)
return data
self.adapter.register_uri('GET',
self.url,
content=_content_cb)
resp = self.session.get(self.url)
self.assertEqual(status_code, resp.status_code)
self.assertEqual(data, resp.content)
self.assertHeaders(resp)
self.assertLastRequest()
def test_text(self):
data = 'testdata'
self.adapter.register_uri('GET',
self.url,
text=data,
headers=self.headers)
resp = self.session.get(self.url)
self.assertEqual(six.b(data), resp.content)
self.assertEqual(six.u(data), resp.text)
self.assertEqual('utf-8', resp.encoding)
self.assertHeaders(resp)
self.assertLastRequest()
def test_text_callback(self):
status_code = 401
data = 'testdata'
def _text_cb(request, context):
context.status_code = status_code
context.headers.update(self.headers)
return six.u(data)
self.adapter.register_uri('GET', self.url, text=_text_cb)
resp = self.session.get(self.url)
self.assertEqual(status_code, resp.status_code)
self.assertEqual(six.u(data), resp.text)
self.assertEqual(six.b(data), resp.content)
self.assertEqual('utf-8', resp.encoding)
self.assertHeaders(resp)
self.assertLastRequest()
def test_json(self):
json_data = {'hello': 'world'}
self.adapter.register_uri('GET',
self.url,
json=json_data,
headers=self.headers)
resp = self.session.get(self.url)
self.assertEqual(six.b('{"hello": "world"}'), resp.content)
self.assertEqual(six.u('{"hello": "world"}'), resp.text)
self.assertEqual(json_data, resp.json())
self.assertEqual('utf-8', resp.encoding)
self.assertHeaders(resp)
self.assertLastRequest()
def test_json_callback(self):
status_code = 401
json_data = {'hello': 'world'}
data = '{"hello": "world"}'
def _json_cb(request, context):
context.status_code = status_code
context.headers.update(self.headers)
return json_data
self.adapter.register_uri('GET', self.url, json=_json_cb)
resp = self.session.get(self.url)
self.assertEqual(status_code, resp.status_code)
self.assertEqual(json_data, resp.json())
self.assertEqual(six.u(data), resp.text)
self.assertEqual(six.b(data), resp.content)
self.assertEqual('utf-8', resp.encoding)
self.assertHeaders(resp)
self.assertLastRequest()
def test_no_body(self):
self.adapter.register_uri('GET', self.url)
resp = self.session.get(self.url)
self.assertEqual(six.b(''), resp.content)
self.assertEqual(200, resp.status_code)
def test_multiple_body_elements(self):
self.assertRaises(RuntimeError,
self.adapter.register_uri,
self.url,
'GET',
content=six.b('b'),
text=six.u('u'))
def test_multiple_responses(self):
inp = [{'status_code': 400, 'text': 'abcd'},
{'status_code': 300, 'text': 'defg'},
{'status_code': 200, 'text': 'hijk'}]
self.adapter.register_uri('GET', self.url, inp)
out = [self.session.get(self.url) for i in range(0, len(inp))]
for i, o in zip(inp, out):
for k, v in six.iteritems(i):
self.assertEqual(v, getattr(o, k))
last = self.session.get(self.url)
for k, v in six.iteritems(inp[-1]):
self.assertEqual(v, getattr(last, k))
def test_callback_optional_status(self):
headers = {'a': 'b'}
def _test_cb(request, context):
context.headers.update(headers)
return ''
self.adapter.register_uri('GET',
self.url,
text=_test_cb,
status_code=300)
resp = self.session.get(self.url)
self.assertEqual(300, resp.status_code)
for k, v in six.iteritems(headers):
self.assertEqual(v, resp.headers[k])
def test_callback_optional_headers(self):
headers = {'a': 'b'}
def _test_cb(request, context):
context.status_code = 300
return ''
self.adapter.register_uri('GET',
self.url,
text=_test_cb,
headers=headers)
resp = self.session.get(self.url)
self.assertEqual(300, resp.status_code)
for k, v in six.iteritems(headers):
self.assertEqual(v, resp.headers[k])
def test_latest_register_overrides(self):
self.adapter.register_uri('GET', self.url, text='abc')
self.adapter.register_uri('GET', self.url, text='def')
resp = self.session.get(self.url)
self.assertEqual('def', resp.text)
def test_no_last_request(self):
self.assertIsNone(self.adapter.last_request)
self.assertEqual(0, len(self.adapter.request_history))
def test_dont_pass_list_and_kwargs(self):
self.assertRaises(RuntimeError,
self.adapter.register_uri,
'GET',
self.url,
[{'text': 'a'}],
headers={'a': 'b'})
def test_empty_string_return(self):
# '' evaluates as False, so make sure an empty string is not ignored.
self.adapter.register_uri('GET', self.url, text='')
resp = self.session.get(self.url)
self.assertEqual('', resp.text)
def test_dont_pass_multiple_bodies(self):
self.assertRaises(RuntimeError,
self.adapter.register_uri,
'GET',
self.url,
json={'abc': 'def'},
text='ghi')
def test_dont_pass_unexpected_kwargs(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
unknown='argument')
def test_dont_pass_unicode_as_content(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
content=six.u('unicode'))
def test_dont_pass_empty_string_as_content(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
content=six.u(''))
def test_dont_pass_bytes_as_text(self):
if six.PY2:
self.skipTest('Cannot enforce byte behaviour in PY2')
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
text=six.b('bytes'))
def test_dont_pass_empty_string_as_text(self):
if six.PY2:
self.skipTest('Cannot enforce byte behaviour in PY2')
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
text=six.b(''))
def test_dont_pass_non_str_as_content(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
content=5)
def test_dont_pass_non_str_as_text(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
text=5)
def test_with_any_method(self):
self.adapter.register_uri(requests_mock.ANY, self.url, text='resp')
for m in ('GET', 'HEAD', 'POST', 'UNKNOWN'):
resp = self.session.request(m, self.url)
self.assertEqual('resp', resp.text)
def test_with_any_url(self):
self.adapter.register_uri('GET', requests_mock.ANY, text='resp')
for u in ('mock://a', 'mock://b', 'mock://c'):
resp = self.session.get(u)
self.assertEqual('resp', resp.text)
def test_with_regexp(self):
self.adapter.register_uri('GET', re.compile('tester.com'), text='resp')
for u in ('mock://www.tester.com/a', 'mock://abc.tester.com'):
resp = self.session.get(u)
self.assertEqual('resp', resp.text)
def test_requests_in_history_on_no_match(self):
self.assertRaises(requests_mock.NoMockAddress,
self.session.get,
self.url)
self.assertEqual(self.url, self.adapter.last_request.url)
def test_requests_in_history_on_exception(self):
def _test_cb(request, ctx):
raise MyExc()
self.adapter.register_uri('GET', self.url, text=_test_cb)
self.assertRaises(MyExc,
self.session.get,
self.url)
self.assertEqual(self.url, self.adapter.last_request.url)
def test_not_called_and_called_count(self):
m = self.adapter.register_uri('GET', self.url, text='resp')
self.assertEqual(0, m.call_count)
self.assertFalse(m.called)
self.assertFalse(m.called_once)
self.assertEqual(0, self.adapter.call_count)
self.assertFalse(self.adapter.called)
self.assertFalse(m.called_once)
def test_called_and_called_count(self):
m = self.adapter.register_uri('GET', self.url, text='resp')
resps = [self.session.get(self.url) for i in range(0, 3)]
for r in resps:
self.assertEqual('resp', r.text)
self.assertEqual(200, r.status_code)
self.assertEqual(len(resps), m.call_count)
self.assertTrue(m.called)
self.assertFalse(m.called_once)
self.assertEqual(len(resps), self.adapter.call_count)
self.assertTrue(self.adapter.called)
self.assertFalse(m.called_once)
def test_adapter_picks_correct_adapter(self):
good = '%s://test3.url/' % self.PREFIX
self.adapter.register_uri('GET',
'%s://test1.url' % self.PREFIX,
text='bad')
self.adapter.register_uri('GET',
'%s://test2.url' % self.PREFIX,
text='bad')
self.adapter.register_uri('GET', good, text='good')
self.adapter.register_uri('GET',
'%s://test4.url' % self.PREFIX,
text='bad')
resp = self.session.get(good)
self.assertEqual('good', resp.text)
def test_adapter_is_connection(self):
url = '%s://test.url' % self.PREFIX
text = 'text'
self.adapter.register_uri('GET', url, text=text)
resp = self.session.get(url)
self.assertEqual(text, resp.text)
self.assertIs(self.adapter, resp.connection)
def test_send_to_connection(self):
url1 = '%s://test1.url/' % self.PREFIX
url2 = '%s://test2.url/' % self.PREFIX
text1 = 'text1'
text2 = 'text2'
self.adapter.register_uri('GET', url1, text=text1)
self.adapter.register_uri('GET', url2, text=text2)
req = requests.Request(method='GET', url=url2).prepare()
resp1 = self.session.get(url1)
self.assertEqual(text1, resp1.text)
resp2 = resp1.connection.send(req)
self.assertEqual(text2, resp2.text)
def test_request_json_with_str_data(self):
dict_req = {'hello': 'world'}
dict_resp = {'goodbye': 'world'}
m = self.adapter.register_uri('POST', self.url, json=dict_resp)
data = json.dumps(dict_req)
resp = self.session.post(self.url, data=data)
self.assertIs(data, m.last_request.body)
self.assertEqual(dict_resp, resp.json())
self.assertEqual(dict_req, m.last_request.json())
def test_request_json_with_bytes_data(self):
dict_req = {'hello': 'world'}
dict_resp = {'goodbye': 'world'}
m = self.adapter.register_uri('POST', self.url, json=dict_resp)
data = json.dumps(dict_req).encode('utf-8')
resp = self.session.post(self.url, data=data)
self.assertIs(data, m.last_request.body)
self.assertEqual(dict_resp, resp.json())
self.assertEqual(dict_req, m.last_request.json())
def test_request_json_with_cb(self):
dict_req = {'hello': 'world'}
dict_resp = {'goodbye': 'world'}
data = json.dumps(dict_req)
def _cb(req, context):
self.assertEqual(dict_req, req.json())
return dict_resp
m = self.adapter.register_uri('POST', self.url, json=_cb)
resp = self.session.post(self.url, data=data)
self.assertEqual(1, m.call_count)
self.assertTrue(m.called_once)
self.assertEqual(dict_resp, resp.json())
def test_raises_exception(self):
self.adapter.register_uri('GET', self.url, exc=MyExc)
self.assertRaises(MyExc,
self.session.get,
self.url)
self.assertTrue(self.adapter.called_once)
self.assertEqual(self.url, self.adapter.last_request.url)
def test_raises_exception_with_body_args_fails(self):
self.assertRaises(TypeError,
self.adapter.register_uri,
'GET',
self.url,
exc=MyExc,
text='fail')
def test_sets_request_matcher_in_history(self):
url1 = '%s://test1.url/' % self.PREFIX
url2 = '%s://test2.url/' % self.PREFIX
text1 = 'text1'
text2 = 'text2'
m1 = self.adapter.register_uri('GET', url1, text=text1)
m2 = self.adapter.register_uri('GET', url2, text=text2)
resp1 = self.session.get(url1)
resp2 = self.session.get(url2)
self.assertEqual(text1, resp1.text)
self.assertEqual(text2, resp2.text)
self.assertEqual(2, self.adapter.call_count)
self.assertFalse(self.adapter.called_once)
self.assertEqual(url1, self.adapter.request_history[0].url)
self.assertEqual(url2, self.adapter.request_history[1].url)
self.assertIs(m1, self.adapter.request_history[0].matcher)
self.assertIs(m2, self.adapter.request_history[1].matcher)
def test_sets_request_matcher_on_exception(self):
m = self.adapter.register_uri('GET', self.url, exc=MyExc)
self.assertRaises(MyExc,
self.session.get,
self.url)
self.assertEqual(self.url, self.adapter.last_request.url)
self.assertIs(m, self.adapter.last_request.matcher)
def test_cookies_from_header(self):
headers = {'Set-Cookie': 'fig=newton; Path=/test; domain=.example.com'}
self.adapter.register_uri('GET',
self.url,
text='text',
headers=headers)
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual(['/test'], resp.cookies.list_paths())
self.assertEqual(['.example.com'], resp.cookies.list_domains())
def test_cookies_from_dict(self):
# This is a syntax we get from requests. I'm not sure i like it.
self.adapter.register_uri('GET',
self.url,
text='text',
cookies={'fig': 'newton', 'sugar': 'apple'})
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
def test_cookies_with_jar(self):
jar = requests_mock.CookieJar()
jar.set('fig', 'newton', path='/foo', domain='.example.com')
jar.set('sugar', 'apple', path='/bar', domain='.example.com')
self.adapter.register_uri('GET', self.url, text='text', cookies=jar)
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
self.assertEqual(set(['/foo', '/bar']), set(resp.cookies.list_paths()))
self.assertEqual(['.example.com'], resp.cookies.list_domains())
def test_cookies_header_with_cb(self):
def _cb(request, context):
val = 'fig=newton; Path=/test; domain=.example.com'
context.headers['Set-Cookie'] = val
return 'text'
self.adapter.register_uri('GET', self.url, text=_cb)
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual(['/test'], resp.cookies.list_paths())
self.assertEqual(['.example.com'], resp.cookies.list_domains())
def test_cookies_from_dict_with_cb(self):
def _cb(request, context):
# converted into a jar by now
context.cookies.set('sugar', 'apple', path='/test')
return 'text'
self.adapter.register_uri('GET',
self.url,
text=_cb,
cookies={'fig': 'newton'})
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
self.assertEqual(['/', '/test'], resp.cookies.list_paths())
def test_cookies_with_jar_cb(self):
def _cb(request, context):
context.cookies.set('sugar',
'apple',
path='/bar',
domain='.example.com')
return 'text'
jar = requests_mock.CookieJar()
jar.set('fig', 'newton', path='/foo', domain='.example.com')
self.adapter.register_uri('GET', self.url, text=_cb, cookies=jar)
resp = self.session.get(self.url)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
self.assertEqual(set(['/foo', '/bar']), set(resp.cookies.list_paths()))
self.assertEqual(['.example.com'], resp.cookies.list_domains())
def test_reading_closed_fp(self):
self.adapter.register_uri('GET', self.url, text='abc')
resp = self.session.get(self.url)
# raw will have been closed during the request reading
self.assertTrue(resp.raw.closed)
data = resp.raw.read()
self.assertIsInstance(data, six.binary_type)
self.assertEqual(0, len(data))
def test_case_sensitive_headers(self):
data = 'testdata'
headers = {'aBcDe': 'FgHiJ'}
self.adapter.register_uri('GET', self.url, text=data)
resp = self.session.get(self.url, headers=headers)
self.assertEqual('GET', self.adapter.last_request.method)
self.assertEqual(200, resp.status_code)
self.assertEqual(data, resp.text)
for k, v in headers.items():
self.assertEqual(v, self.adapter.last_request.headers[k])
def test_case_sensitive_history(self):
self.adapter._case_sensitive = True
data = 'testdata'
netloc = 'examPlE.CoM'
path = '/TesTER'
query = 'aBC=deF'
mock_url = '%s://%s%s' % (self.PREFIX, netloc.lower(), path)
request_url = '%s://%s%s?%s' % (self.PREFIX, netloc, path, query)
# test that the netloc is ignored when actually making the request
self.adapter.register_uri('GET', mock_url, text=data)
resp = self.session.get(request_url)
self.assertEqual('GET', self.adapter.last_request.method)
self.assertEqual(200, resp.status_code)
self.assertEqual(data, resp.text)
# but even still the mixed case parameters come out in history
self.assertEqual(netloc, self.adapter.last_request.netloc)
self.assertEqual(path, self.adapter.last_request.path)
self.assertEqual(query, self.adapter.last_request.query)

View File

@ -1,75 +0,0 @@
# 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.
import requests
import six
import requests_mock
from requests_mock.tests import base
class FailMatcher(object):
def __init___(self):
self.called = False
def __call__(self, request):
self.called = True
return None
def match_all(request):
return requests_mock.create_response(request, content=six.b('data'))
def test_a(request):
if 'a' in request.url:
return match_all(request)
return None
class CustomMatchersTests(base.TestCase):
def assertMatchAll(self, resp):
self.assertEqual(200, resp.status_code)
self.assertEqual(resp.text, six.u('data'))
@requests_mock.Mocker()
def test_custom_matcher(self, mocker):
mocker.add_matcher(match_all)
resp = requests.get('http://any/thing')
self.assertMatchAll(resp)
@requests_mock.Mocker()
def test_failing_matcher(self, mocker):
failer = FailMatcher()
mocker.add_matcher(match_all)
mocker.add_matcher(failer)
resp = requests.get('http://any/thing')
self.assertMatchAll(resp)
self.assertTrue(failer.called)
@requests_mock.Mocker()
def test_some_pass(self, mocker):
mocker.add_matcher(test_a)
resp = requests.get('http://any/thing')
self.assertMatchAll(resp)
self.assertRaises(requests_mock.NoMockAddress,
requests.get,
'http://other/thing')

View File

@ -1,39 +0,0 @@
# 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.
import requests
import requests_mock
from requests_mock.contrib import fixture
from requests_mock.tests import base
class MockingTests(base.TestCase):
def setUp(self):
super(MockingTests, self).setUp()
self.mocker = self.useFixture(fixture.Fixture())
def test_failure(self):
self.assertRaises(requests_mock.NoMockAddress,
requests.get,
'http://www.google.com')
def test_basic(self):
test_url = 'http://www.google.com/'
self.mocker.register_uri('GET', test_url, text='response')
resp = requests.get(test_url)
self.assertEqual('response', resp.text)
self.assertEqual(test_url, self.mocker.last_request.url)
def test_fixture_has_normal_attr_error(self):
self.assertRaises(AttributeError, lambda: self.mocker.unknown)

View File

@ -1,286 +0,0 @@
# 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.
import re
from requests_mock import adapter
from requests_mock.tests import base
ANY = adapter.ANY
class TestMatcher(base.TestCase):
def match(self,
target,
url,
matcher_method='GET',
request_method='GET',
complete_qs=False,
headers=None,
request_data=None,
request_headers={},
additional_matcher=None,
real_http=False,
case_sensitive=False):
matcher = adapter._Matcher(matcher_method,
target,
[],
complete_qs=complete_qs,
additional_matcher=additional_matcher,
request_headers=request_headers,
real_http=real_http,
case_sensitive=case_sensitive)
request = adapter._RequestObjectProxy._create(request_method,
url,
headers,
data=request_data)
return matcher._match(request)
def assertMatch(self,
target=ANY,
url='http://example.com/requests-mock',
matcher_method='GET',
request_method='GET',
**kwargs):
self.assertEqual(True,
self.match(target,
url,
matcher_method=matcher_method,
request_method=request_method,
**kwargs),
'Matcher %s %s failed to match %s %s' %
(matcher_method, target, request_method, url))
def assertMatchBoth(self,
target=ANY,
url='http://example.com/requests-mock',
matcher_method='GET',
request_method='GET',
**kwargs):
self.assertMatch(target,
url,
matcher_method=matcher_method,
request_method=request_method,
**kwargs)
self.assertMatch(url,
target,
matcher_method=request_method,
request_method=matcher_method,
**kwargs)
def assertNoMatch(self,
target=ANY,
url='http://example.com/requests-mock',
matcher_method='GET',
request_method='GET',
**kwargs):
self.assertEqual(False,
self.match(target,
url,
matcher_method=matcher_method,
request_method=request_method,
**kwargs),
'Matcher %s %s unexpectedly matched %s %s' %
(matcher_method, target, request_method, url))
def assertNoMatchBoth(self,
target=ANY,
url='http://example.com/requests-mock',
matcher_method='GET',
request_method='GET',
**kwargs):
self.assertNoMatch(target,
url,
matcher_method=matcher_method,
request_method=request_method,
**kwargs)
self.assertNoMatch(url,
target,
matcher_method=request_method,
request_method=matcher_method,
**kwargs)
def assertMatchMethodBoth(self, matcher_method, request_method, **kwargs):
url = 'http://www.test.com'
self.assertMatchBoth(url,
url,
request_method=request_method,
matcher_method=matcher_method,
**kwargs)
def assertNoMatchMethodBoth(self,
matcher_method,
request_method,
**kwargs):
url = 'http://www.test.com'
self.assertNoMatchBoth(url,
url,
request_method=request_method,
matcher_method=matcher_method,
**kwargs)
def test_url_matching(self):
self.assertMatchBoth('http://www.test.com',
'http://www.test.com')
self.assertMatchBoth('http://www.test.com',
'http://www.test.com/')
self.assertMatchBoth('http://www.test.com/abc',
'http://www.test.com/abc')
self.assertMatchBoth('http://www.test.com:5000/abc',
'http://www.test.com:5000/abc')
self.assertNoMatchBoth('https://www.test.com',
'http://www.test.com')
self.assertNoMatchBoth('http://www.test.com/abc',
'http://www.test.com')
self.assertNoMatchBoth('http://test.com',
'http://www.test.com')
self.assertNoMatchBoth('http://test.com',
'http://www.test.com')
self.assertNoMatchBoth('http://test.com/abc',
'http://www.test.com/abc/')
self.assertNoMatchBoth('http://test.com/abc/',
'http://www.test.com/abc')
self.assertNoMatchBoth('http://test.com:5000/abc/',
'http://www.test.com/abc')
self.assertNoMatchBoth('http://test.com/abc/',
'http://www.test.com:5000/abc')
def test_subset_match(self):
self.assertMatch('/path', 'http://www.test.com/path')
self.assertMatch('/path', 'http://www.test.com/path')
self.assertMatch('//www.test.com/path', 'http://www.test.com/path')
self.assertMatch('//www.test.com/path', 'https://www.test.com/path')
def test_query_string(self):
self.assertMatch('/path?a=1&b=2',
'http://www.test.com/path?a=1&b=2')
self.assertMatch('/path?a=1',
'http://www.test.com/path?a=1&b=2',
complete_qs=False)
self.assertNoMatch('/path?a=1',
'http://www.test.com/path?a=1&b=2',
complete_qs=True)
self.assertNoMatch('/path?a=1&b=2',
'http://www.test.com/path?a=1')
def test_method_match(self):
self.assertNoMatchMethodBoth('GET', 'POST')
self.assertMatchMethodBoth('GET', 'get')
self.assertMatchMethodBoth('GeT', 'geT')
def test_match_ANY_url(self):
self.assertMatch(ANY, 'http://anything')
self.assertMatch(ANY, 'http://somethingelse')
self.assertNoMatch(ANY, 'http://somethingelse', request_method='POST')
def test_match_ANY_method(self):
for m in ('GET', 'POST', 'HEAD', 'OPTION'):
self.assertMatch('http://www.test.com',
'http://www.test.com',
matcher_method=ANY,
request_method=m)
self.assertNoMatch('http://www.test.com',
'http://another',
matcher_method=ANY)
def test_match_with_regex(self):
r1 = re.compile('test.com/a')
r2 = re.compile('/b/c')
self.assertMatch(r1, 'http://mock.test.com/a/b')
self.assertMatch(r1, 'http://test.com/a/')
self.assertMatch(r1, 'mock://test.com/a/b')
self.assertNoMatch(r1, 'mock://test.com/')
self.assertMatch(r2, 'http://anything/a/b/c/d')
self.assertMatch(r2, 'mock://anything/a/b/c/d')
def test_match_with_headers(self):
self.assertMatch('/path',
'http://www.test.com/path',
headers={'A': 'abc', 'b': 'def'},
request_headers={'a': 'abc'})
self.assertMatch('/path',
'http://www.test.com/path',
headers={'A': 'abc', 'b': 'def'})
self.assertNoMatch('/path',
'http://www.test.com/path',
headers={'A': 'abc', 'b': 'def'},
request_headers={'b': 'abc'})
self.assertNoMatch('/path',
'http://www.test.com/path',
headers={'A': 'abc', 'b': 'def'},
request_headers={'c': 'ghi'})
# headers should be key insensitive and value sensitive, we have no
# choice here because they go into an insensitive dict.
self.assertMatch('/path',
'http://www.test.com/path',
headers={'aBc': 'abc', 'DEF': 'def'},
request_headers={'abC': 'abc'})
self.assertNoMatch('/path',
'http://www.test.com/path',
headers={'abc': 'aBC', 'DEF': 'def'},
request_headers={'abc': 'Abc'})
def test_case_sensitive_ignored_for_netloc_and_protocol(self):
for case_sensitive in (True, False):
self.assertMatch('http://AbC.CoM',
'http://aBc.CoM',
case_sensitive=case_sensitive)
self.assertMatch('htTP://abc.com',
'hTTp://abc.com',
case_sensitive=case_sensitive)
self.assertMatch('htTP://aBC.cOm',
'hTTp://AbC.Com',
case_sensitive=case_sensitive)
def assertSensitiveMatch(self, target, url, **kwargs):
self.assertMatch(target, url, case_sensitive=False, **kwargs)
self.assertNoMatch(target, url, case_sensitive=True, **kwargs)
def test_case_sensitive_paths(self):
self.assertSensitiveMatch('http://abc.com/pAtH', 'http://abc.com/path')
self.assertSensitiveMatch('/pAtH', 'http://abc.com/path')
def test_case_sensitive_query(self):
self.assertSensitiveMatch('http://abc.com/path?abCD=efGH',
'http://abc.com/path?abCd=eFGH')
self.assertSensitiveMatch('http://abc.com/path?abcd=efGH',
'http://abc.com/path?abcd=eFGH')
def test_additional_matcher(self):
def test_match_body(request):
return 'hello' in request.text
self.assertMatch(request_method='POST',
matcher_method='POST',
request_data='hello world',
additional_matcher=test_match_body)
self.assertNoMatch(request_method='POST',
matcher_method='POST',
request_data='goodbye world',
additional_matcher=test_match_body)

View File

@ -1,443 +0,0 @@
# 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.
import mock
import requests
import requests_mock
from requests_mock import exceptions
from requests_mock import response
from requests_mock.tests import base
original_send = requests.Session.send
class MockerTests(base.TestCase):
def assertMockStarted(self):
self.assertNotEqual(original_send, requests.Session.send)
def assertMockStopped(self):
self.assertEqual(original_send, requests.Session.send)
def _do_test(self, m):
self.assertMockStarted()
matcher = m.register_uri('GET', 'http://www.test.com', text='resp')
resp = requests.get('http://www.test.com')
self.assertEqual('resp', resp.text)
return matcher
def test_multiple_starts(self):
mocker = requests_mock.Mocker()
self.assertMockStopped()
mocker.start()
self.assertMockStarted()
self.assertRaises(RuntimeError, mocker.start)
mocker.stop()
self.assertMockStopped()
mocker.stop()
def test_with_context_manager(self):
self.assertMockStopped()
with requests_mock.Mocker() as m:
self._do_test(m)
self.assertMockStopped()
@mock.patch('requests.adapters.HTTPAdapter.send')
@requests_mock.mock(real_http=True)
def test_real_http(self, real_send, mocker):
url = 'http://www.google.com/'
test_text = 'real http data'
test_bytes = test_text.encode('utf-8')
# using create_response is a bit bootstrappy here but so long as it's
# coming from HTTPAdapter.send it's ok
req = requests.Request(method='GET', url=url)
real_send.return_value = response.create_response(req.prepare(),
status_code=200,
content=test_bytes)
resp = requests.get(url)
self.assertEqual(1, real_send.call_count)
self.assertEqual(url, real_send.call_args[0][0].url)
self.assertEqual(test_text, resp.text)
self.assertEqual(test_bytes, resp.content)
@requests_mock.mock()
def test_with_test_decorator(self, m):
self._do_test(m)
@requests_mock.mock(kw='mock')
def test_with_mocker_kwargs(self, **kwargs):
self._do_test(kwargs['mock'])
def test_with_decorator(self):
@requests_mock.mock()
def inner(m):
self.assertMockStarted()
self._do_test(m)
self.assertMockStopped()
inner()
self.assertMockStopped()
def test_with_decorator_called_multiple_times(self):
@requests_mock.Mocker()
def inner(arg1, m):
self._do_test(m)
self.assertEquals(
len(m.request_history), 1,
"Failed to provide clean mock on subsequent calls"
)
inner('a')
# if we call the same decorated method again should get
# a new request mock
inner('b')
def test_with_class_decorator(self):
outer = self
@requests_mock.mock()
class Decorated(object):
def test_will_be_decorated(self, m):
outer.assertMockStarted()
outer._do_test(m)
def will_not_be_decorated(self):
outer.assertMockStopped()
decorated_class = Decorated()
self.assertMockStopped()
decorated_class.test_will_be_decorated()
self.assertMockStopped()
decorated_class.will_not_be_decorated()
self.assertMockStopped()
def test_with_class_decorator_and_custom_kw(self):
outer = self
@requests_mock.mock(kw='custom_m')
class Decorated(object):
def test_will_be_decorated(self, **kwargs):
outer.assertMockStarted()
outer._do_test(kwargs['custom_m'])
def will_not_be_decorated(self):
outer.assertMockStopped()
decorated_class = Decorated()
self.assertMockStopped()
decorated_class.test_will_be_decorated()
self.assertMockStopped()
decorated_class.will_not_be_decorated()
self.assertMockStopped()
@mock.patch.object(requests_mock.mock, 'TEST_PREFIX', 'foo')
def test_with_class_decorator_and_custom_test_prefix(self):
outer = self
@requests_mock.mock()
class Decorated(object):
def foo_will_be_decorated(self, m):
outer.assertMockStarted()
outer._do_test(m)
def will_not_be_decorated(self):
outer.assertMockStopped()
decorated_class = Decorated()
self.assertMockStopped()
decorated_class.foo_will_be_decorated()
self.assertMockStopped()
decorated_class.will_not_be_decorated()
self.assertMockStopped()
@requests_mock.mock()
def test_query_string(self, m):
url = 'http://test.url/path'
qs = 'a=1&b=2'
m.register_uri('GET', url, text='resp')
resp = requests.get("%s?%s" % (url, qs))
self.assertEqual('resp', resp.text)
self.assertEqual(qs, m.last_request.query)
self.assertEqual(['1'], m.last_request.qs['a'])
self.assertEqual(['2'], m.last_request.qs['b'])
@requests_mock.mock()
def test_mock_matcher_attributes(self, m):
matcher = self._do_test(m)
self.assertEqual(1, matcher.call_count)
self.assertEqual(1, m.call_count)
self.assertTrue(matcher.called)
self.assertTrue(matcher.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
self.assertEqual(m.request_history, matcher.request_history)
self.assertIs(m.last_request, matcher.last_request)
def test_copy(self):
mocker = requests_mock.mock(kw='foo', real_http=True)
copy_of_mocker = mocker.copy()
self.assertIsNot(copy_of_mocker, mocker)
self.assertEqual(copy_of_mocker._kw, mocker._kw)
self.assertEqual(copy_of_mocker._real_http, mocker._real_http)
class MockerHttpMethodsTests(base.TestCase):
URL = 'http://test.com/path'
TEXT = 'resp'
def assertResponse(self, resp):
self.assertEqual(self.TEXT, resp.text)
@requests_mock.Mocker()
def test_mocker_request(self, m):
method = 'XXX'
mock_obj = m.request(method, self.URL, text=self.TEXT)
resp = requests.request(method, self.URL)
self.assertResponse(resp)
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_get(self, m):
mock_obj = m.get(self.URL, text=self.TEXT)
self.assertResponse(requests.get(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_options(self, m):
mock_obj = m.options(self.URL, text=self.TEXT)
self.assertResponse(requests.options(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_head(self, m):
mock_obj = m.head(self.URL, text=self.TEXT)
self.assertResponse(requests.head(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_post(self, m):
mock_obj = m.post(self.URL, text=self.TEXT)
self.assertResponse(requests.post(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_put(self, m):
mock_obj = m.put(self.URL, text=self.TEXT)
self.assertResponse(requests.put(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_patch(self, m):
mock_obj = m.patch(self.URL, text=self.TEXT)
self.assertResponse(requests.patch(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_delete(self, m):
mock_obj = m.delete(self.URL, text=self.TEXT)
self.assertResponse(requests.delete(self.URL))
self.assertTrue(mock_obj.called)
self.assertTrue(mock_obj.called_once)
self.assertTrue(m.called)
self.assertTrue(m.called_once)
@requests_mock.Mocker()
def test_mocker_real_http_and_responses(self, m):
self.assertRaises(RuntimeError,
m.get,
self.URL,
text='abcd',
real_http=True)
@requests_mock.Mocker()
def test_mocker_real_http(self, m):
data = 'testdata'
uri1 = 'fake://example.com/foo'
uri2 = 'fake://example.com/bar'
uri3 = 'fake://example.com/baz'
m.get(uri1, text=data)
m.get(uri2, real_http=True)
self.assertEqual(data, requests.get(uri1).text)
# This should fail because requests can't get an adapter for mock://
# but it shows that it has tried and would have made a request.
self.assertRaises(requests.exceptions.InvalidSchema,
requests.get,
uri2)
# This fails because real_http is not set on the mocker
self.assertRaises(exceptions.NoMockAddress,
requests.get,
uri3)
# do it again to make sure the mock is still in place
self.assertEqual(data, requests.get(uri1).text)
@requests_mock.Mocker(case_sensitive=True)
def test_case_sensitive_query(self, m):
data = 'testdata'
query = {'aBcDe': 'FgHiJ'}
m.get(self.URL, text=data)
resp = requests.get(self.URL, params=query)
self.assertEqual('GET', m.last_request.method)
self.assertEqual(200, resp.status_code)
self.assertEqual(data, resp.text)
for k, v in query.items():
self.assertEqual([v], m.last_request.qs[k])
@mock.patch.object(requests_mock.Mocker, 'case_sensitive', True)
def test_global_case_sensitive(self):
with requests_mock.mock() as m:
data = 'testdata'
query = {'aBcDe': 'FgHiJ'}
m.get(self.URL, text=data)
resp = requests.get(self.URL, params=query)
self.assertEqual('GET', m.last_request.method)
self.assertEqual(200, resp.status_code)
self.assertEqual(data, resp.text)
for k, v in query.items():
self.assertEqual([v], m.last_request.qs[k])
def test_nested_mocking(self):
url1 = 'http://url1.com/path1'
url2 = 'http://url2.com/path2'
url3 = 'http://url3.com/path3'
data1 = 'data1'
data2 = 'data2'
data3 = 'data3'
with requests_mock.mock() as m1:
r1 = m1.get(url1, text=data1)
resp1a = requests.get(url1)
self.assertRaises(exceptions.NoMockAddress, requests.get, url2)
self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
self.assertEqual(data1, resp1a.text)
# call count = 3 because there are 3 calls above, url 1-3
self.assertEqual(3, m1.call_count)
self.assertEqual(1, r1.call_count)
with requests_mock.mock() as m2:
r2 = m2.get(url2, text=data2)
self.assertRaises(exceptions.NoMockAddress, requests.get, url1)
resp2a = requests.get(url2)
self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
self.assertEqual(data2, resp2a.text)
with requests_mock.mock() as m3:
r3 = m3.get(url3, text=data3)
self.assertRaises(exceptions.NoMockAddress,
requests.get,
url1)
self.assertRaises(exceptions.NoMockAddress,
requests.get,
url2)
resp3 = requests.get(url3)
self.assertEqual(data3, resp3.text)
self.assertEqual(3, m3.call_count)
self.assertEqual(1, r3.call_count)
resp2b = requests.get(url2)
self.assertRaises(exceptions.NoMockAddress, requests.get, url1)
self.assertEqual(data2, resp2b.text)
self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
self.assertEqual(3, m1.call_count)
self.assertEqual(1, r1.call_count)
self.assertEqual(6, m2.call_count)
self.assertEqual(2, r2.call_count)
self.assertEqual(3, m3.call_count)
self.assertEqual(1, r3.call_count)
resp1b = requests.get(url1)
self.assertEqual(data1, resp1b.text)
self.assertRaises(exceptions.NoMockAddress, requests.get, url2)
self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
self.assertEqual(6, m1.call_count)
self.assertEqual(2, r1.call_count)
@requests_mock.mock()
def test_mocker_additional(self, m):
url = 'http://www.example.com'
good_text = 'success'
def additional_cb(req):
return 'hello' in req.text
m.post(url, additional_matcher=additional_cb, text=good_text)
self.assertEqual(good_text,
requests.post(url, data='hello world').text)
self.assertRaises(exceptions.NoMockAddress,
requests.post,
url,
data='goodbye world')

View File

@ -1,123 +0,0 @@
# 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.
import uuid
import requests
import requests_mock
from requests_mock.tests import base
class RequestTests(base.TestCase):
def setUp(self):
super(RequestTests, self).setUp()
self.mocker = requests_mock.Mocker()
self.addCleanup(self.mocker.stop)
self.mocker.start()
def do_request(self, **kwargs):
method = kwargs.pop('method', 'GET')
url = kwargs.pop('url', 'http://test.example.com/path')
status_code = kwargs.pop('status_code', 200)
data = uuid.uuid4().hex
m = self.mocker.register_uri(method,
url,
text=data,
status_code=status_code)
resp = requests.request(method, url, **kwargs)
self.assertEqual(status_code, resp.status_code)
self.assertEqual(data, resp.text)
self.assertTrue(m.called_once)
return m.last_request
def test_base_params(self):
req = self.do_request(method='GET', status_code=200)
self.assertIs(None, req.allow_redirects)
self.assertIs(None, req.timeout)
self.assertIs(True, req.verify)
self.assertIs(None, req.cert)
# actually it's an OrderedDict, but equality works fine
self.assertEqual({}, req.proxies)
def test_allow_redirects(self):
req = self.do_request(allow_redirects=False, status_code=300)
self.assertFalse(req.allow_redirects)
def test_timeout(self):
timeout = 300
req = self.do_request(timeout=timeout)
self.assertEqual(timeout, req.timeout)
def test_verify_false(self):
verify = False
req = self.do_request(verify=verify)
self.assertIs(verify, req.verify)
def test_verify_path(self):
verify = '/path/to/cacerts.pem'
req = self.do_request(verify=verify)
self.assertEqual(verify, req.verify)
def test_certs(self):
cert = ('/path/to/cert.pem', 'path/to/key.pem')
req = self.do_request(cert=cert)
self.assertEqual(cert, req.cert)
self.assertTrue(req.verify)
def test_proxies(self):
proxies = {'http': 'foo.bar:3128',
'http://host.name': 'foo.bar:4012'}
req = self.do_request(proxies=proxies)
self.assertEqual(proxies, req.proxies)
self.assertIsNot(proxies, req.proxies)
def test_hostname_port_http(self):
req = self.do_request(url='http://host.example.com:81/path')
self.assertEqual('host.example.com:81', req.netloc)
self.assertEqual('host.example.com', req.hostname)
self.assertEqual(81, req.port)
def test_hostname_port_https(self):
req = self.do_request(url='https://host.example.com:8080/path')
self.assertEqual('host.example.com:8080', req.netloc)
self.assertEqual('host.example.com', req.hostname)
self.assertEqual(8080, req.port)
def test_hostname_default_port_http(self):
req = self.do_request(url='http://host.example.com/path')
self.assertEqual('host.example.com', req.netloc)
self.assertEqual('host.example.com', req.hostname)
self.assertEqual(80, req.port)
def test_hostname_default_port_https(self):
req = self.do_request(url='https://host.example.com/path')
self.assertEqual('host.example.com', req.netloc)
self.assertEqual('host.example.com', req.hostname)
self.assertEqual(443, req.port)
def test_to_string(self):
req = self.do_request(url='https://host.example.com/path')
self.assertEqual('GET https://host.example.com/path', str(req))

View File

@ -1,112 +0,0 @@
# 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.
import six
from requests_mock import adapter
from requests_mock import exceptions
from requests_mock import response
from requests_mock.tests import base
class ResponseTests(base.TestCase):
def setUp(self):
super(ResponseTests, self).setUp()
self.method = 'GET'
self.url = 'http://test.url/path'
self.request = adapter._RequestObjectProxy._create(self.method,
self.url,
{})
def create_response(self, **kwargs):
return response.create_response(self.request, **kwargs)
def test_create_response_body_args(self):
self.assertRaises(RuntimeError,
self.create_response,
raw='abc',
body='abc')
self.assertRaises(RuntimeError,
self.create_response,
text='abc',
json={'a': 1})
def test_content_type(self):
self.assertRaises(TypeError, self.create_response, text=55)
self.assertRaises(TypeError, self.create_response, text={'a': 1})
# this only works on python 2 because bytes is a string
if six.PY3:
self.assertRaises(TypeError, self.create_response, text=six.b(''))
def test_text_type(self):
self.assertRaises(TypeError, self.create_response, content=six.u('t'))
self.assertRaises(TypeError, self.create_response, content={'a': 1})
self.assertRaises(TypeError, self.create_response, content=six.u(''))
def test_json_body(self):
data = {'a': 1}
resp = self.create_response(json=data)
self.assertEqual('{"a": 1}', resp.text)
self.assertIsInstance(resp.text, six.string_types)
self.assertIsInstance(resp.content, six.binary_type)
self.assertEqual(data, resp.json())
def test_body_body(self):
value = 'data'
body = six.BytesIO(six.b(value))
resp = self.create_response(body=body)
self.assertEqual(value, resp.text)
self.assertIsInstance(resp.text, six.string_types)
self.assertIsInstance(resp.content, six.binary_type)
def test_setting_connection(self):
conn = object()
resp = self.create_response(connection=conn)
self.assertIs(conn, resp.connection)
def test_send_from_no_connection(self):
resp = self.create_response()
self.assertRaises(exceptions.InvalidRequest,
resp.connection.send, self.request)
def test_cookies_from_header(self):
# domain must be same as request url to pass policy check
headers = {'Set-Cookie': 'fig=newton; Path=/test; domain=.test.url'}
resp = self.create_response(headers=headers)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual(['/test'], resp.cookies.list_paths())
self.assertEqual(['.test.url'], resp.cookies.list_domains())
def test_cookies_from_dict(self):
# This is a syntax we get from requests. I'm not sure i like it.
resp = self.create_response(cookies={'fig': 'newton',
'sugar': 'apple'})
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
def test_cookies_with_jar(self):
jar = response.CookieJar()
jar.set('fig', 'newton', path='/foo', domain='.test.url')
jar.set('sugar', 'apple', path='/bar', domain='.test.url')
resp = self.create_response(cookies=jar)
self.assertEqual('newton', resp.cookies['fig'])
self.assertEqual('apple', resp.cookies['sugar'])
self.assertEqual(set(['/foo', '/bar']), set(resp.cookies.list_paths()))
self.assertEqual(['.test.url'], resp.cookies.list_domains())

View File

@ -1,2 +0,0 @@
requests>=1.1
six

View File

@ -1 +0,0 @@
pbr

View File

@ -1,41 +0,0 @@
[metadata]
name = requests-mock
author = Jamie Lennox
author-email = jamielennox@gmail.com
summary = Mock out responses from the requests package
description-file = README.rst
license = Apache-2
license-file = LICENSE
home-page = https://requests-mock.readthedocs.org/
classifier =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
Intended Audience :: Information Technology
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Software Development :: Testing
test_suite = requests_mock.tests
[files]
packages = requests_mock
[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source
[bdist_wheel]
universal = 1
[extras]
fixture =
fixtures

View File

@ -1,8 +0,0 @@
#!/usr/bin/env python
from setuptools import setup
setup(
setup_requires=['pbr'],
pbr=True,
)

View File

@ -1,5 +0,0 @@
fixtures
mock
sphinx>=1.1.2,!=1.2.0,<1.3
testrepository>=0.0.18
testtools

81
tox.ini
View File

@ -1,81 +0,0 @@
[tox]
envlist = py36,py35,py34,py27,pypy,pep8
[testenv]
setenv =
PYTHONPATH = {toxinidir}:{toxinidir}/requests-mock
commands = python setup.py testr
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
pbr
[testenv:pep8]
commands = flake8 requests_mock
deps =
flake8
{[testenv]deps}
[testenv:venv]
commands = {posargs}
[testenv:coverage]
commands =
coverage erase
coverage run --source requests_mock -m testtools.run discover
coverage html
deps =
coverage
{[testenv]deps}
[testenv:requests123]
commands =
{envbindir}/pip install "requests==1.2.3"
python setup.py testr --testr-args='{posargs}'
[testenv:doctest]
# note this only works under python 3 because of unicode literals
commands =
python -m doctest README.rst
[testenv:sphinx-doctest]
# note this only works under python 3 because of unicode literals
commands =
mkdir build/sphinx/doctest
sphinx-build -b doctest docs build/sphinx/doctest
deps =
pbr
{[testenv]deps}
[testenv:keystoneclient-tip]
deps =
six
-r{toxinidir}/test-requirements.txt
-egit+https://git.openstack.org/openstack/python-keystoneclient#egg=python-keystoneclient
-egit+https://github.com/kennethreitz/requests.git#egg=requests
changedir = {envdir}/src/python-keystoneclient
commands =
{envbindir}/pip install -r requirements.txt -r test-requirements.txt
{envbindir}/pip install pbr -t {envsitepackagesdir} # work around pbr being build installed in {toxinidir}
python setup.py testr --testr-args='{posargs}'
[testenv:novaclient-tip]
deps =
six
-r{toxinidir}/test-requirements.txt
-egit+https://git.openstack.org/openstack/python-novaclient#egg=python-novaclient
-egit+https://github.com/kennethreitz/requests.git#egg=requests
changedir = {envdir}/src/python-novaclient
commands =
{envbindir}/pip install -r requirements.txt -r test-requirements.txt
{envbindir}/pip install pbr -t {envsitepackagesdir} # work around pbr being build installed in {toxinidir}
python setup.py testr --testr-args='{posargs}'
[testenv:docs]
commands = python setup.py build_sphinx
[testenv:requests-tip]
deps =
six
-egit+https://github.com/kennethreitz/requests.git#egg=requests
-r{toxinidir}/test-requirements.txt