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:
parent
10a3470dac
commit
67663054a5
@ -1,7 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = requests_mock
|
||||
omit = requests_mock/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
50
.gitignore
vendored
50
.gitignore
vendored
@ -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
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/requests-mock.git
|
@ -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
|
29
.zuul.yaml
29
.zuul.yaml
@ -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
180
LICENSE
@ -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.
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include LICENSE
|
92
README.rst
92
README.rst
@ -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".
|
||||
|
@ -1,3 +0,0 @@
|
||||
coverage
|
||||
sphinx
|
||||
wheel
|
177
doc/Makefile
177
doc/Makefile
@ -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."
|
@ -1,7 +0,0 @@
|
||||
requests_mock
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
requests_mock
|
@ -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:
|
242
doc/make.bat
242
doc/make.bat
@ -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
|
@ -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
|
@ -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)}
|
@ -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
|
@ -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
|
@ -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.
|
@ -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`
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -1 +0,0 @@
|
||||
.. include:: ../../README.rst
|
@ -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']
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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]`
|
@ -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',
|
||||
]
|
@ -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']
|
@ -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
|
@ -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)
|
@ -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"""
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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)
|
@ -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')
|
@ -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)
|
@ -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)
|
@ -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')
|
@ -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))
|
@ -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())
|
@ -1,2 +0,0 @@
|
||||
requests>=1.1
|
||||
six
|
@ -1 +0,0 @@
|
||||
pbr
|
41
setup.cfg
41
setup.cfg
@ -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
|
8
setup.py
8
setup.py
@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True,
|
||||
)
|
@ -1,5 +0,0 @@
|
||||
fixtures
|
||||
mock
|
||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||
testrepository>=0.0.18
|
||||
testtools
|
81
tox.ini
81
tox.ini
@ -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
|
Loading…
Reference in New Issue
Block a user